当提到并发你会想到什么?
没错,就是这位兄弟Promise.all
Promise.all怎么用的
const createPromiser = delay => {
return new Promise(resolve => setTimeout(() => resolve(delay), delay))
}
const p1 = createPromiser(100)
const p2 = createPromiser(200)
const p3 = createPromiser(300)
Promise.all([p1,p2,p3]).then(value => {
console.log(value); // [ 100, 200, 300 ] 按顺序来输出每个promise返回值
})
Promise.all实现
// 验证promise
function isPromise (promise) {
return promise && typeof promise.then === 'function'
}
Promise.all = function (promises) {
// Promise.all是个函数,返回一个新的promise
return new Promise((resolve,reject) => {
let le = promises.length
// 存储每个promise resolve后的value
let result = new Array(le)
// 定义个计数器,很重要。只有当计数器 = le时候,所有的promise就执行完成了。可以直接resolve(result)
let count = 0
for(let i = 0; i < le; i++) {
// 取出每一项
const promise = promises[i]
// 可能当前项并不是一个promise,这里要做个验证。
if(isPromise(promise)) {
promise.then(value => {
// 闭包记录每个promise的fullfiled状态的值
result[i] = value
// 每次执行promose resolve异步回调都要让计数器+1。
if(++count === le) {
resolve(result)
}
},reject)
}else {
// 闭包记录非promise的值
result[i] = promise
// 这里也是一样,非promise也要让计数器+1
if(++count === le) {
resolve(result)
}
}
}
})
}
- 从代码里我们可以看出,通过for循环,把所有的promise都一次性执行完成。
假如一个页面要请求100次接口,你会怎么做?
- 上次请求完成后再请求下一个,一个请求平均时间100ms,那么100个请求就是 100 * 100ms = 10000ms = 10s。这要花费10s的时间才能全部请求完成。在这10s内页面一直处于Loading状态,这是不是用户体验很不好。
// 模拟实现请求
async function requestAll () {
console.time()
for(let i = 0; i < 100; i++) {
await createPromiser(100)
}
console.timeEnd() // default: 10608.35400390625 ms
}
requestAll()
- 借用
Promise.all一次性发送100个请求。虽然可以实现快速的数据获取,但是并发数过大可能会导致服务器压力过大。
async function requestAll () {
console.time()
await Promise.all(Array.from({length: 100}, () => createPromiser(100)))
console.timeEnd() // default: 110.302734375 ms
}
requestAll()
- 能不能实现一个方法,能控制并发呢?
并发控制器的实现
class Scheduler {
constructor(max){
// 最大并发数
this.max = max
// 存储promise resolve的队列
this.resolves = []
// 计数器
this.count = 0
}
async add(createPromiser){
// 当计数器>=最大并发数时候,就中断后面函数的执行。
if(this.count >= this.max) {
// 借助于Promise await,只要让当前这个promsie一直处于pending状态就可以实现
await new Promise(resolve => this.resolves.push(resolve))
}
// 每次执行一个promsie任务,计数器就+1
this.count++
const result = await createPromiser().then(value => [null,value], (err) => [err,null])
// 执行完成后,计数器-1
this.count--
// 当前promise行完成之后,就从resolves队列中取出一个resolve来执行。
const resolve = this.resolves.shift()
resolve && resolve()
return result
}
}
// 最大并发数3
const scheduler = new Scheduler(3)
const promises = []
Array.from({length:100}, () => {
// 这里返回的还是个promise
// 我们可以使用promise.all来实现
const p = scheduler.add(() => createPromiser(100))
promises.push(p)
})
console.time()
Promise.all(promises).then(() => {
console.timeEnd() // default: 1835.571044921875 ms
})