Promise.all执行后添加Promise被忽略,该如何解决?
Promise.all()等待后续添加的Promise? 这是个很容易踩中的Promise使用误区——Promise.all()在调用的那一刻就固定了要等待的Promise集合,它会立即读取传入数组的当前状态(相当于拍了个快照),之后你对数组的任何修改(比如push新的Promise)都不会影响它的执行逻辑。这就是为什么你的代码会输出All done2000:调用Promise.all(pr)时,数组里只有那个2秒后resolve的Promise,后面加的4秒Promise根本没被纳入等待范围。
要实现“让后续添加的Promise也被等待”,你需要换一种方式管理这些Promise,而不是直接用原生的Promise.all(),下面提供两种实用方案:
方案1:封装一个动态Promise管理类
可以自定义一个类来维护所有需要等待的Promise,每次添加新Promise时更新总等待逻辑:
class DynamicPromiseWaiter { constructor() { // 维护一个总Promise,初始为已完成状态 this.totalPromise = Promise.resolve(); } // 添加新的Promise到等待队列 add(promise) { // 更新总Promise:等待之前所有Promise完成 + 新Promise完成 this.totalPromise = Promise.all([this.totalPromise, promise]); } // 获取最终的等待Promise wait() { return this.totalPromise; } } // 示例使用 const start = Date.now(); const waiter = new DynamicPromiseWaiter(); // 添加第一个2秒的Promise waiter.add(new Promise(resolve => { setTimeout(() => resolve(), 2000); })); // 开始等待所有Promise完成 waiter.wait().then(() => { console.log(`All done${Date.now() - start}`); // 最终输出All done4000 }); // 延迟1秒后添加第二个4秒的Promise setTimeout(() => { waiter.add(new Promise(resolve => { setTimeout(() => resolve(), 4000); })); }, 1000);
原理说明
这个类通过totalPromise变量持续跟踪所有需要等待的任务:每次调用add()时,都会把新Promise和之前的totalPromise组合成新的Promise.all(),这样最终的wait()返回的Promise会等待所有添加过的Promise完成,不管是在调用wait()之前还是之后添加的(只要在总Promise完成前添加)。
方案2:用变量直接维护总Promise
如果不需要封装类,也可以用一个变量直接管理总等待逻辑,写法更简洁:
const start = Date.now(); let allTasks = Promise.resolve(); // 初始为已完成的Promise // 添加第一个2秒的任务 allTasks = Promise.all([allTasks, new Promise(resolve => { setTimeout(() => resolve(), 2000); })]); // 等待所有任务完成 allTasks.then(() => { console.log(`All done${Date.now() - start}`); // 输出All done4000 }); // 延迟添加第二个4秒的任务 setTimeout(() => { allTasks = Promise.all([allTasks, new Promise(resolve => { setTimeout(() => resolve(), 4000); })]); }, 1000);
原理说明
和方案1的核心逻辑一致:allTasks变量每次都会被更新为包含之前所有任务和新任务的Promise组合,最终的then回调会等待所有这些任务全部完成。
关键提醒
不管用哪种方案,都要注意:必须在总Promise完成前添加新的Promise,如果总Promise已经resolve了,再添加新的Promise不会触发重新等待。
内容的提问来源于stack exchange,提问作者Alexey Vodolazhchenko

