如何在持续添加Promise时,待所有Promise settled后关闭加载遮罩
PromiseBroker加载遮罩提前关闭的解决方案
问题场景
我写了一个PromiseBroker类用来管理API请求的Promise,目的是所有请求完成后关闭加载遮罩,但目前遇到遮罩提前关闭的问题:
- 添加Promise A - 显示加载遮罩
- Promise A 决议 - 关闭加载遮罩
- 添加Promise B - 显示加载遮罩
- 添加Promise C
- Promise B 决议 - 关闭加载遮罩
- Promise C 决议 - 这才是预期的遮罩关闭时机
我希望所有Promise(包括调用allSettled后新增的)都完成时才关闭遮罩,推测是因为等待期间Promise集合变化,旧的allSettled任务依然会触发关闭逻辑,想知道如何取消/覆盖旧的allSettled调用,只保留最新的。
原代码
class PromiseBroker { #promises = {}; #overlayEle = document.getElementById('loading-overlay'); constructor(){ } get promises(){ return this.#promises; } setPromise(key, value){ this.promises[key] = value; this.#overlayEle.classList.remove('d-none'); Promise.allSettled(Object.values(this.promises)).then(results => { this.#overlayEle.classList.add('d-none') }); } }
问题分析
每次调用setPromise都会创建一个新的Promise.allSettled实例,这些实例独立运行。比如添加Promise B后,会基于当时的[Promise B]集合启动一个allSettled任务,后续添加Promise C时又会基于[Promise B, Promise C]启动新任务。当Promise B完成时,第一个任务就会触发关闭遮罩,而此时Promise C还在运行,这就导致了提前关闭的问题。
解决方案
我们可以通过一个任务标识跟踪最新的allSettled任务,让只有最新任务的回调才执行遮罩关闭操作,旧任务的回调直接忽略。具体实现如下:
class PromiseBroker { #promises = {}; #overlayEle = document.getElementById('loading-overlay'); #currentTaskId = 0; // 跟踪最新任务的标识 constructor(){ } get promises(){ return this.#promises; } setPromise(key, value){ this.promises[key] = value; this.#overlayEle.classList.remove('d-none'); // 生成新的任务ID,标记当前任务为最新 const taskId = ++this.#currentTaskId; Promise.allSettled(Object.values(this.promises)).then(() => { // 仅当当前任务是最新的才执行关闭操作 if (taskId === this.#currentTaskId) { this.#overlayEle.classList.add('d-none'); } }); } }
关键逻辑说明
- 新增
#currentTaskId变量,每次调用setPromise时递增它,生成唯一的任务标识 - 在
allSettled的回调中,先判断当前任务的ID是否等于最新的#currentTaskId:- 如果相等,说明这是最后一次添加Promise后启动的任务,此时所有Promise都已完成,执行关闭遮罩
- 如果不等,说明已经有新的Promise被添加,当前任务是旧任务,直接忽略回调
这样就能保证只有当所有已添加的Promise都完成,且没有后续新Promise添加时,才会关闭遮罩。
内容的提问来源于stack exchange,提问作者LattePrincess
相关产品推荐
相关产品推荐

