You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

递归创建Promise时Promise.all提前完成问题求助

问题分析与解决方案

你的问题主要出在两个核心点上:Promise构造器的async反模式,以及全局Promise数组的时机错误,导致Promise.all只等待了初始的一批Promise,完全没等到递归产生的后续任务。

核心问题拆解

  1. new Promise(async (resolve) => {})是典型反模式
    async函数本身就会返回一个Promise,再用new Promise包裹完全多余,还会导致状态管理混乱——你里面的await会让递归逻辑异步执行,但外层的Promise可能在递归任务还没启动时就被resolve了,直接跳过了等待子任务的步骤。

  2. 全局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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:48:26