前言
当你搜到这篇文章的时候,说明你在面试中遇到过这种问题,但是好多文章用例不清晰,只处理了并发的事情,没有处理后续回掉逻辑,本文结合一道面试题展开介绍如何解决并发问题。
需求
- 多个请求,要求并发处理
- 处理resolve,reject情况
- 用promise去实现
场景
先构建场景。封装一个fetch函数用来调用接口,接口有一半的可能返回成功,一半失败。
const fetch = (url) => {
return () => new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5 ? resolve(url) : reject(url)
}, 2000);
})
}
然后构建多个请求:
const request = SuperRequest(3)
for (let i = 1; i <= 5; i++) {
request(`/test${i}`)
.then(res => {
console.log(res);
})
.catch(err => {
console.log('err:', err);
})
}
SuperRequest传入的是并发值,需要去完成SuperRequest函数。
思路
思路肯定是很清晰的,使用闭包去实现,需要有值记录running中的请求使其小于并发值。难点可能是当并发中的请求执行完毕之后,怎么去增加一个新的请求,并正确触发其成功或失败的回掉。
实现方案
代码中需要注意两件事情:
- tasks是全部任务,在push的时候需要把对应的resolve,reject带入,这样当这个任务执行的时候可以去触发resolve,reject的回掉。
- 使用then,catch去触发resolve,reject,使用finally意味着当前请求执行完毕,需要去添加一个新请求
function SuperRequest(limit) {
let tasks = []
let runingCount = 0
const _run = () => {
while (runingCount < limit && tasks.length > 0) {
const { task, resolve, reject } = tasks.shift()
runingCount++
Promise.resolve(task())
.then(resolve)
.catch(reject)
.finally(() => {
runingCount--
_run()
})
}
}
return (url) => {
return new Promise((resolve, reject) => {
const task = fetch(url)
tasks.push({ task, resolve, reject })
_run()
})
}
}
function SuperRequest(limit) {
let tasks = []
let runingCount = 0
const _run = () => {
while (runingCount < limit && tasks.length > 0) {
const { task, resolve, reject } = tasks.shift()
runingCount++
Promise.resolve(task())
.then(resolve)
.catch(reject)
.finally(() => {
runingCount--
_run()
})
}
}
return (url) => {
return new Promise((resolve, reject) => {
const task = fetch(url)
tasks.push({ task, resolve, reject })
_run()
})
}
}