您现在的位置是:首页 >技术杂谈 >网络请求实战-实战Fetch和Promise相关的架构网站首页技术杂谈
网络请求实战-实战Fetch和Promise相关的架构
简介网络请求实战-实战Fetch和Promise相关的架构
目录
Promise神器(承诺)
Promise+Coding示例
- 代表异步求值的过程和结果
promise链式调用,可以一直promise下去
// example 01
const promise = new Promise((resolve, reject) => {
resolve(100)
}).then(data => {
console.log(data)
})
// 100
// example 02
const promise = new Promise((resolve, reject) => {
resolve(100)
}).then(data => {
console.log(data)
return 'abc' // 直接返回一个值
})
.then(data => { // 此处不用执行promise了,直接fulfilled了一个'abc
console.log(data)
})
// 100 abc
// example 03
function wait(ms = 1000, data) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(data)
}, ms)
})
}
const promise = new Promise((resolve, reject) => {
resolve(100)
}).then(data => {
console.log(data) // 100
return wait(1000, 'abc') // 等待1s,返'abc'
})
.then(data => {
console.log(data) // 等待1000ms,打印'abc'
})
// example04
const promise = new Promise((resolve, reject) => {
reject("some error")
}).then(data => {
console.log("1", data) // 不执行
}).catch(ex => {
console.error(ex) // some error
return "GO"
}).then(data => {
console.log(data) // GO
})
// example05
function wait(ms = 1000, data){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(data)
}, ms)
})
}
async function foo(){
console.log('--begin--')
const one = await wait(1000, 1)
console.log('--tick 1--') // 等待一秒
const two = await wait(1000, 2)
console.log('--tick 2--') // 再等待一秒
console.log(one, two) // 1, 2
await Promise.reject('some error')
try{
await Promise.reject('some error')
} catch(ex) {
console.log(ex) // some error
}
}
foo()
// example 6
function wait(ms = 1000, data){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(data)
}, ms)
})
}
// 工厂方法
Promise.all([wait(200, 1), wait(100, 2)])
.then(data => {
console.log('all', data) // 等2个promise都结束,返回这个数组[1,2]
})
Promise.race([wait(200, 1), wait(100, 2)])
.then(data => {
console.log('race', data) // race是在这个执行promise数组中返回第一个拿到的值
})
Promise常见用法
- resolve & reject
- Chain (链执行)
- 并发和竞争(all 和 race)
- 异常处理 (try catch)
简单的promise
const PENDING = 1
const FULLFILLED = 2
const REJECTED = 3
class Promise{
constructor(executor){
this.state = PENDING
const resolver = (value) => {
if(this.state === PENDING) {
this.state = FULLFILLED
this.value = value
}
for(let [onFullFill, resolve] of this.fullfills) {
const x = onFullFill(this.value)
resolve(x)
}
}
const rejector = () => {
this.state = REJECTED
}
this.fullfills = []
executor(resolver, rejector)
}
then(onFullfill) {
return new Promise((resolve, rejector) => {
switch(this.state) {
case FULLFILLED:
const x = onFullfill(this.value)
resolve(x)
break
case PENDING:
this.fullfills.push([onFullfill, resolve])
break
}
})
}
}
new Promise((resolve) => {
setTimeout(() => {
resolve('123')
})
}).then(data => {
console.log(data) // 123
return '456'
}).then(data => {
console.log(data) // 456
return data
})
Fetch的基本用法
fetch
一个让处理http pipeline更容易的工具(MDN)
- 返回Promise
- Resolve发生在网络通信正常(404,500也是resolve)
- Reject发生在网络通信异常
- 默认不接受cookie(需要设置)
存在队头阻塞,服务端负载充足
Fetch基本用法
- GET/POST/PUT/DELETE
- Headers
- Cookie
- 缓存
fetch("/product", {
method: "POST",
headers : {
"Content-Type" : "application/json"
},
body: JSON.stringify({ name: "123苹果" }),
}).then((resp) => console.log(resp.status))
fetch("/product", {
method: "POST",
headers : {
"Content-Type" : "application/json"
},
body: JSON.stringify({ name: "".padStart(100000, "A")}),
}).then((resp) => console.log(resp.status))
// node端 需要引用实例
const fetch = require('node-fetch')
const promise = fetch('https://www.baidu.com', {
method: 'POST',
headers: {
'Content-Type': "application/json",
},
credentials: 'include', // 设置之后可以接收服务端的Cookie
})
async function foo() {
const resp = await fetch("http://www.baidu.com", {
headers: {
'user-agent': "Mozillia"
}
})
const text = await resp.text()
console.log(text)
}
foo()
// 缓存
fetch("https://www.baidu.com", {cache: 'force-cache'})
Fetch+Promise场景举例
1.指数补偿,专门应付移动端频繁网络波动的
按照指数的时间倍数重复发送请求
- 0ms
- 200ms
- 400ms
- 800ms
- 1600ms
- 3200ms
- fail
// 示例程序
const fetch = require('node-fetch')
function request(url){
let resolved = false
let t = 1
return new Promise((resolve) => {
function doFetch(){
if(resolved || t > 16) {
return
}
fetch(url).then((resp) => {
return resp.text()
})
.then(data => {
if(!resolved) {
resolved = true
resolve(data)
console.log('t=', t)
}
})
.catch(ex => {
console.error(ex)
})
setTimeout(() => {
doFetch()
t *= 2
}, t * 100)
}
doFetch()
})
}
request('http://www.baidu.com')
.then(data => {
console.log(data.length)
})
setTimeout(() => {
}, 3000)
// 有缺陷,所有请求都发送了
function wait(ms, f) {
return new Promise((resolve) => {
setTimeout(()=> {
resolve(f())
}, ms)
})
}
function request(url) {
Promise.race([
fetch(url),
wait(100, ()=> fetch(url) ),
wait(200, ()=> fetch(url) ),
wait(400, ()=> fetch(url) ),
wait(800, ()=> fetch(url) ),
wait(1600, ()=> fetch(url) ),
])
}
request('http://www.baidu.com')
.then(resp => {
console.log(resp)
})
setTimeout(() => {
}, 3000)
2.并发处理和时间窗口,底层前端请求优化的
多个资源并发请求(Promise.all)
基于时间窗口过滤重复请求
const fetch = require('node-fetch')
function hash(args) {
return args.join(',')
}
function window_it(f, time = 50) {
let w = {}
let flag = false
return (...args) => {// 参数传递
return new Promise((resolve) => {
if (!w[hash(args)]) {
w[hash(args)] = {
func: f,
args,
resolvers: [],
}
}
if (!flag) {
flag = true
setTimeout(() => {
Object.keys(w).forEach((key) => {
const { func, args, resolvers } = w[key]
const promise = func(...args)
.then((resp) => resp.text())
.then((text) =>
resolvers.map((r) => {
r(text)
})
)
})
flag = false
}, time)
}
console.log(args)
w[hash(args)].resolvers.push(resolve)
})
}
}
const request = window_it(fetch, 20)
request('http://www.baidu.com')
.then(txt => console.log(txt.length))
request('http://www.baidu.com')
.then(txt => console.log(txt.length))
request('http://www.baidu.com')
.then(txt => console.log(txt.length))
request('http://www.zhihu.com')
.then(txt => console.log(txt.length))
request('http://www.baidu.com')
.then(txt => console.log(txt.length))
request('http://www.baidu.com')
.then(txt => console.log(txt.length))
小结
- 为什么不教Axios? 因为fetch是浏览器提供的标准
- 优化问题要工程化处理(所有页面的优化,请求优化,页面大小优化,白屏的优化【需要用到算法,数据结构和相关的架构知识,是针对一类问题提供工具】)
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。