ES5实现JS循环中Promise无论成败均串行调用同一函数
用ES5语法实现Promise串行调用(无论成功/失败都继续)
你原来的写法有两个核心问题:
var的变量提升导致i的值异常:var声明的变量是函数级作用域,循环里所有的i指向同一个引用。当setTimeout触发时,循环早已结束,i会变成5,最终调用的都是myFunction(5)。- 没有实现串行执行:循环里直接调用
myFunction(i)会让所有Promise同时启动,是并行而非串行的逻辑。
下面用两种ES5兼容的方式解决这个问题:
方法1:构建Promise链式调用
我们可以先初始化一个已决议的Promise作为起点,然后通过循环依次把每个myFunction的调用挂载到链上。不管上一个Promise是resolve还是reject,都继续执行下一个任务。
function myFunction(i) { return new Promise(function(resolve, reject) { setTimeout(function() { console.log(i); // 可模拟失败场景:比如i===2时触发reject // if(i === 2) reject(new Error('Test error at ' + i)); resolve(i); }, i * 1000); }); } // 初始化一个已resolve的Promise作为链式调用的起点 var promiseChain = Promise.resolve(); // 循环构建串行链 for (var i = 0; i < 5; i++) { // 用立即执行函数(IIFE)保存当前i的快照,避免变量提升问题 (function(currentI) { promiseChain = promiseChain .then(function() { return myFunction(currentI); }) .catch(function(err) { // 可选:在这里处理错误,比如打印错误信息 console.error('Error occurred:', err); // 无论失败与否,都继续执行下一个任务 return myFunction(currentI); }); })(i); }
关键细节:
- 立即执行函数创建了独立的作用域,把当前循环的
i值保存为currentI,避免后续循环修改导致的变量污染。 - 每次循环都会更新
promiseChain,将下一个任务挂载到链的末尾,确保任务按顺序串行执行。
方法2:递归调用(更直观的串行逻辑)
如果觉得链式调用不够直观,递归是另一种清晰的实现方式:每次完成当前任务后,再递归调用下一个任务。
function myFunction(i) { return new Promise(function(resolve, reject) { setTimeout(function() { console.log(i); // 可模拟失败场景:比如i===3时触发reject // if(i === 3) reject(new Error('Failed at index ' + i)); resolve(i); }, i * 1000); }); } function serialCall(currentIndex) { // 递归终止条件:所有任务执行完成 if (currentIndex >= 5) { return Promise.resolve(); } return myFunction(currentIndex) .then(function() { // 成功后继续执行下一个任务 return serialCall(currentIndex + 1); }) .catch(function(err) { // 失败后打印错误,仍继续执行下一个任务 console.error('Error at index ' + currentIndex + ': ', err); return serialCall(currentIndex + 1); }); } // 启动串行调用 serialCall(0);
关键细节:
serialCall函数负责单个索引的任务执行,完成后递归调用下一个索引,天然保证串行顺序。- 无需处理
var的变量提升问题,因为每次递归的currentIndex都是独立的函数参数。
两种方法都能满足你的需求:每次循环迭代串行调用myFunction,无论前一个Promise成功还是失败,都继续执行下一个任务,你可以根据习惯选择适合的实现方式。
内容的提问来源于stack exchange,提问作者Akash khan
相关产品推荐
相关产品推荐

