递归创建Promise时Promise.all提前完成问题求助
问题分析与解决方案
你的问题主要出在两个核心点上:Promise构造器的async反模式,以及全局Promise数组的时机错误,导致Promise.all只等待了初始的一批Promise,完全没等到递归产生的后续任务。
核心问题拆解
new Promise(async (resolve) => {})是典型反模式
async函数本身就会返回一个Promise,再用new Promise包裹完全多余,还会导致状态管理混乱——你里面的await会让递归逻辑异步执行,但外层的Promise可能在递归任务还没启动时就被resolve了,直接跳过了等待子任务的步骤。全局
this.promised数组的时机完全不对
在update函数里,你调用parseItem后立刻执行Promise.all(this.promised),但parseItem里的API请求是异步的,递归调用parseItem并push到数组的操作,要等await this.MediaWiki.ask(...)完成后才会执行。这意味着Promise.all执行时,数组里只有最开始的几个Promise,后续递归产生的Promise根本没被加进去,自然会提前结束。
修复后的代码
// 移除全局的this.promised,不需要再用它收集Promise parseItem(relation) { // 如果已经处理过,直接返回resolved的Promise if (relation.wasResolved === true) { return Promise.resolve(); } // 直接用async函数返回Promise,去掉多余的new Promise包裹 return (async () => { console.log('Parsing ' + relation.displaytitle); let result = await this.MediaWiki.ask('[[Has source element::' + relation.fulltext + ']]|?Has target element'); // 收集当前层级所有递归的子任务Promise const childPromises = []; // 遍历API返回结果 for (let k in result) { let targetElements = result[k].printouts['Has target element']; // 遍历每个目标元素 for (let index in targetElements) { let itemIndex = this.getIndex(targetElements[index]); // 只处理已知元素 if (itemIndex !== -1) { this.elementsInView[itemIndex].parent = relation; // 把递归调用的Promise收集到当前层级的数组中 childPromises.push(this.parseItem(this.elementsInView[itemIndex])); } } } // 等待所有递归子任务完成后,再标记当前relation为已处理 await Promise.all(childPromises); relation.wasResolved = true; return true; })(); } update() { // 收集所有顶级的parseItem任务Promise const topLevelPromises = []; // 遍历视图中的每个元素 for (let index in this.elementsInView) { let element = this.elementsInView[index]; // 如果元素有分配的hostId,加入顶级任务队列 if (element.host !== -1) { topLevelPromises.push(this.parseItem(element)); } } // 等待所有顶级任务(包括它们的所有递归子任务)全部完成 Promise.all(topLevelPromises).then(value => { this.finalize(); }); } finalize() { for (let index in this.elementsInView) { let element = this.elementsInView[index]; if (element.wasResolved === true) { console.log(element); } } }
关键改动说明
- 移除全局Promise数组:每个
parseItem内部独立收集自己的递归子任务Promise,用Promise.all(childPromises)等待子任务全部完成,确保当前任务只有在所有子任务结束后才会resolve。 - 去掉Promise构造器的async包裹:直接用IIFE的async函数返回Promise,避免状态混乱,让异步逻辑的依赖关系更清晰。
- update函数收集顶级Promise:
Promise.all等待的是所有顶级任务,而每个顶级任务又会自动等待自己的所有递归子任务,这样整个递归结构的所有异步任务都会被正确等待。
这样修改后,Promise.all会等到所有层级的异步任务(包括递归产生的)全部完成,才会触发finalize执行。
内容的提问来源于stack exchange,提问作者Youri
相关产品推荐
相关产品推荐

