转载自一文教你如何实现并发请求的失败自动重试及重试次数限制_javescript重试代码-CSDN博客
let second2 = 0
let second3 = 0
function request1() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("请求完毕11")
resolve(111)
}, 1000);
})
}
function request2() {
return new Promise((resolve, reject) => {
if(second2 < 2) {
setTimeout(() => {
console.log("请求完毕22")
reject(222)
second2++
}, 2000);
} else {
setTimeout(() => {
console.log("请求完毕22")
resolve(222)
}, 2000);
}
})
}
function request3() {
return new Promise((resolve, reject) => {
if(second3 < 1) {
setTimeout(() => {
console.log("请求完毕33")
reject(333)
second3++
}, 3000)
} else {
setTimeout(() => {
console.log("请求完毕33")
resolve(333)
}, 3000);
}
})
}
// 实现一个RequestParallelWithRetry类
class RequestParallelWithRetry {
requestList
retryCount
map = {}
result = []
// 将传入的请求方法进行标记并保存它们的关系,便于后续请求失败时可以利用这个标记来再次调用这个方法
constructor(requestList = [], retryCount = 3) {
this.requestList = requestList
this.retryCount = retryCount
// 需要给每个方法添加标记
this.requestList.forEach((item, index) => {
item._index = index
this.map[ index ] = item
})
}
// 实现RequestParallelWithRetry类的send方法,它用来并发发送请求,并且要求能够得知每个请求的执行结果
send() {
return new Promise((resolve) => {
// 1. 将所有的网络请求进行包装,使得其返回结果中携带有_index
// 2. 发送所有的网络请求
let queue = []
this.requestList.forEach(request => {
// 3. 记录每个请求的重试次数
request._retryCount = 0
queue.push(this._wrapRequest(request))
})
// 我们在send方法内部递归调用sendAllSettled方法,sendAllSettled方法做的事情很简单:
// 将queue队列中的请求发送出去
// 将失败的请求重新添加到队列中,再次发送
const sendAllSettled = () => {
// 因为我们这里请求的返回结果都是一个promise对象,因此我们可以利用Promise.allSettled方法来获取所有promise的最终结果:
Promise.allSettled(queue).then((value) => {
// 3. 清除发送队列
queue = []
console.log('所有请求都完成了', value);
// 4. 检查是否存在失败的请求
value.forEach(result => {
if (result.status === "fulfilled") {
const { value, index } = result.value
this.result[ index ] = { value, status: 'fulfilled' }
}
else if (result.status === "rejected") {
const { reason, index } = result.reason
const request = this.map[ index ]
if (request._retryCount < this.retryCount) {
queue.push(this._wrapRequest(request))
request._retryCount++
} else {
this.result[ index ] = { reason, status: 'rejected' }
}
}
})
// 5. 检查队列中是否还需要发送请求
if (queue.length) {
sendAllSettled()
} else {
resolve(this.result)
}
})
}
sendAllSettled()
})
}
// 包装请求的执行结果
// 我们已经知道了我们重复执行那些失败的请求直到成功,但是还存在一个问题:现在我们只能知道失败请求的结果,但是我们无法根据这个结果来找到原先执行请求的方法。因此,我们需要对传入的请求进行一层包装,使得执行的返回结果中包含一个标记,我们能根据这个标记找到这个请求方法:
_wrapRequest(request) {
return new Promise((resolve, reject) => {
request().then((value) => {
resolve({
value,
index: request._index
})
}).catch(reason => {
reject({
reason,
index: request._index
})
})
})
}
}
new RequestParallelWithRetry([ request1, request2, request3 ]).send().then((value) => {
console.log(value);
})