循环中Promise回调在循环结束后执行,如何处理同时规避Eslint警告?
问题原因分析
这是JavaScript事件循环机制的固有执行逻辑:
- 所有同步代码优先级最高,会先完整执行完当前调用栈内的所有同步任务,才会处理异步任务队列的内容
- 你写的for循环属于同步代码,循环内的
console.log('fileExists')是同步执行的,而axios.head().then()的回调属于异步微任务,会被暂时推入微任务队列等待调度 - 等你整个for循环跑完5次、所有同步代码执行完毕后,才会依次执行微任务队列里的5个axios请求回调,所以你会看到先输出5次fileExists的日志,再输出所有接口返回的日志
- 另外你这个写法的轮询逻辑实际上是失效的:因为循环是同步跑完的,等第一个回调把
fileExists设为true的时候,5次请求早就全部发出去了,根本达不到“成功就终止后续请求”的效果
符合Promise最佳实践的解决方案
Eslint的no-await-in-loop规则本质是提醒开发者不要把可并行的异步操作搞成串行,你这个轮询场景本身就需要串行等待,不属于规则要禁止的错误用法,但如果不想加disable注释,可以用递归的Promise链式调用实现,完全规避循环相关的Eslint警告:
// 封装独立的轮询逻辑,无循环结构,不会触发循环相关Eslint规则 const pollFileReady = (maxRetries = 5, currentWaitTime = 100) => { // 重试次数耗尽,抛出超时错误 if (maxRetries <= 0) return Promise.reject(new Error('文件同步超时')); return axios.head(response.data.data.url) .then(res => { if (res.status === 200) { console.log('Status 200, 文件同步完成'); return response.data; } // 404时进入下一次重试 Vue.$log.debug(`Image not ready for download ${res.status}`); const nextWaitTime = maxRetries > 1 ? 200 : 1000; console.log(`waitTime = ${nextWaitTime}`); // 等待指定时长后递归发起下一次查询 return new Promise(resolve => setTimeout(resolve, currentWaitTime)) .then(() => pollFileReady(maxRetries - 1, nextWaitTime)); }); }; // 直接调用即可,需要拿结果的话前面加await return pollFileReady();
该方案的优势:
- 完全没有用到循环结构,自然规避
no-await-in-loop、no-loop-func两类Eslint警告 - 严格按照串行逻辑执行,前一次请求结果返回后才会判断是否需要发起下一次请求,成功就直接返回结果,不会发送多余请求
- 无外部可变状态,所有重试次数、等待时长都通过递归参数传递,逻辑更内聚,没有外部变量污染
- 符合原生Promise的链式调用最佳实践,可读性和可维护性更高
内容的提问来源于stack exchange,提问作者Leos Literak
相关产品推荐
相关产品推荐

