基于tiny-async-pool改造带并发限制、异常重试的异步请求逻辑疑问
代码原理说明
基础版racer逻辑解析
你提到的这段代码是异步并发池控制并发数的核心逻辑:
const racer = pro.then(() => queue.splice(queue.indexOf(racer), 1))
它的运行逻辑拆解如下:
pro是你调用iteratorFn生成的单个请求Promise,代表一个异步请求的生命周期racer是pro链式调用后返回的新Promise,它唯一对应当前这个请求,作为并发队列queue的占位元素- 这里利用了JS回调的延迟执行特性:
then里的回调函数只有等pro请求成功后才会执行,此时racer变量已经完成赋值,且已经被push到queue数组中,queue.indexOf(racer)可以准确找到当前请求在并发队列中的下标 - 回调执行
queue.splice的作用就是把当前已完成的请求从并发队列中移除,空出一个并发位给后续新的请求,以此维持同时运行的请求数始终不超过poolLimit的限制 - 后续的
await Promise.race(queue)就是等待并发队列中任意一个请求完成、队列空出位置,再继续往队列里加新的请求
加catch的改造逻辑解析
你新增的catch回调是适配你的错误重试场景的必要修改,原因如下:
const racer = pro .then(() => queue.splice(queue.indexOf(racer), 1)) .catch(() => queue.splice(queue.indexOf(racer), 1))
- 原始
tiny-async-pool底层用Promise.all,只要有一个请求失败就直接终止整个池的运行,因此不需要处理proreject的场景 - 你的改造支持失败重试,请求reject属于预期内的正常情况:如果不给
racer加catch,当pro请求失败时,then回调不会执行,racer本身会变成rejected状态,此时await Promise.race(queue)会直接抛出未捕获异常,导致整个异步池提前中断 - 加了catch之后,不管
pro是成功还是失败,只要请求走到终态,都会执行splice把自己从并发队列中移除,同时catch没有抛出新错误,racer最终永远是resolved状态,不会触发Promise.race的异常中断 - 这里catch只做队列清理,没有吞掉原始请求的错误:你是把所有原始请求
pro都存在了promises数组里,后面用Promise.allSettled统一收集所有请求的成功/失败结果,完全不影响错误重试逻辑的执行
内容的提问来源于stack exchange,提问作者Álvaro
相关产品推荐
相关产品推荐

