递归创建新的承诺

时间:2018-05-23 14:17:34

标签: javascript promise es6-promise

我正在尝试使用Promise.all函数来处理递归结构。

从for循环中调用的函数也可以创建promise,但是Promise.all函数似乎在工作完成之前就完成了,我不知道为什么。

this.promised = [];
parseItem( relation ) {

    if( relation.wasResolved === true ) {
        return Promise.resolve();
    }

    return new Promise(async (resolve) => {

        console.log( 'Parsing ' + relation.displaytitle );

        let result = await this.MediaWiki.ask('[[Has source element::'+ relation.fulltext +']]|?Has target element');

        // Loop over the API results
        for( let k in result ) {

            let targetElements = result[k].printouts['Has target element'];

            // Loop over every target element
            for( let index in targetElements) {

                let itemIndex = this.getIndex( targetElements[index] );

                // Only resolve known elements
                if( itemIndex !== -1 ) {
                    // Set parent && resolve childs
                    this.elementsInView[itemIndex].parent = relation;
                    // Resolve
                    this.promised.push( this.parseItem( this.elementsInView[itemIndex] ) );
                }
            }
        }

        // Set wasResolved to true
        relation.wasResolved = true;

        resolve(true);
    });
}

update() {

    // Loop over every element in the view
    for( let index in this.elementsInView ) {
        let element = this.elementsInView[index];
        // If the viewItem has a assigned hostId, add it to the queue
        if( element.host !== -1 ) {
            this.promised.push( this.parseItem( element ) );
        }
    }

    Promise.all(this.promised).then(value =>{
        this.finalize();
    });

}

finalize() {
    for( let index in this.elementsInView ) {
        let element = this.elementsInView[index];
        if( element.wasResolved === true ) {
            console.log( element );
        }
    }
}

1 个答案:

答案 0 :(得分:1)

问题来自于您尝试在this.promised之间共享update()以及parseItem()内的异步行为。

要解决此问题,您可以从代表内部创建的每个Promise的{​​{1}}返回parseItem()

例如,简化版本:

Promise

正如您所看到的,在async parseItem(relation) { const result = await ask(relation); return await Promise.all(result.map(i => { const relations = i.relations; return Promise.all(relations.map(parseItem)); })); } 内,我们会在解决parseItem()之前等待所有内容递归。

要将此应用于您的代码,它可能如下所示:

Promise
但是,我无法保证这会奏效。这取决于这个数据结构的性质以及在解决所有这些承诺的同时修改它的效果。

提出问题的主要问题是这一行:

async parseItem(relation) {
  if (relation.wasResolved === true) {
    return true;
  }

  console.log('Parsing ' + relation.displaytitle);

  const result = await this.MediaWiki.ask('[[Has source element::' + relation.fulltext + ']]|?Has target element');

  const nestedPromises = result.map(el => {
    const targetElements = el.printouts['Has target element'];

    // This returns an array of an array of promises
    return targetElements
      .map(tel => this.getIndex(tel))
      .filter(index => index !== -1)
      .map(index => {
        this.elementsInView[index].parent = relation;

        // This returns a promise
        return this.parseItem(this.elementsInView[index]);
      });
  });

  // Easy way to flatten array of arrays
  const promises = [].concat.apply([], nestedPromises);
  await Promise.all(promises);

  // Set wasResolved to true
  relation.wasResolved = true;

  return true;
}

这对this.elementsInView[index].parent = relation; 有什么影响?由于这种突变,我现在关心的是,以parseItem传递给parseItem的多个项目是否以交错方式运行可能同时修改它们所依赖的数据

至少就原始问题而言,这种方法应该可以帮助您等待所有承诺的完成。