Promise.all递归

时间:2017-01-19 12:38:19

标签: javascript recursion promise

我有以下程序(它应该是可执行的),我在处理对涉及许多异步操作的方法的递归调用时遇到问题。

作为该计划的起点,我有一个test()方法。它触发第一次调用它传递_add()对象(嵌套“元素”树)的root

_add方法负责将嵌套元素添加到storage(我在这里用一个简单的数组表示)。为了做到这一点,我编写了函数,以便它在所有子元素上调用它。

存储元素是一种异步操作。因此,我使用一系列承诺来保存所有子元素的“尚未可用”值。

test()显示,我无法在此处正确处理同步。在第一次调用add()之前返回的承诺结算之前,所有元素都应该已插入存储中。这显然不是程序输出显示的情况。

当然,由于子节点的parentId引用其父节点的id,因此对我来说重要的是存储操作的发生顺序与探索元素树的顺序相同。

我希望在尝试从storage检索元素时遇到完全相同的问题。

非常感谢任何帮助。

提前致谢。

(() => {
  const storage = [];
  const root = {
    _type: "root",
    title: "root element",
    gravity: null,
    children: [
      {
        _type: "space",
        title: "my first space",
        gravity: 0,
        children: [
          {
            _type: "bundle",
            title: null,
            gravity: 0,
            children: [
              {
                _type: "vis",
                title: "my first vis",
                gravity: 0,
                visualization: {
                  url: "http://dummyvis.com/#8766"
                },
                children: null
              },
              {
                _type: "feature",
                title: "my feature 1",
                gravity: 1,
                feature: {
                  componentType: "CPT_FEATURE1"
                },
                children: []
              },
              {
                _type: "feature",
                title: "my feature 2",
                gravity: 1,
                feature: {
                  componentType: "CPT_FEATURE2"
                },
                children: []
              }
            ]
          }
        ]
      }
    ]
  };

  const store = (element, parentId) => {
    return new Promise( (resolve, reject) => {

      storage.push({id:element.id, _type:element._type, gravity: element.gravity, parent:parentId, created_date:Date.now() });

      setTimeout( () => resolve(element), 500);
    });
  }

  const _add = (element, recurse, parentId) => {
    console.log(element._type);
    if(!element._type) 
      throw new Error("an element must declare a key '_type'");
    parentId = parentId || null;

    return new Promise( (resolve, reject) => {
      const promises = [];

      promises.push( new Promise( (resolve, reject) => {
        store(element, parentId)
          .then(element => {
            resolve(element);
          })
          .catch(err => reject(err));
      }));

      Promise.all(promises)
        .then( res => {
          if(recurse && element.children.length > 0) {
            element.children.forEach( child => {
              _add(child, recurse, element.id);
            });
          }
          resolve(element);
        })
        .catch( err => reject(err));
    });
  }

  const test = () => {
    _add(root, true)
      .then( res => {
        console.log("----------------------------------------------------------");
        console.log("Test completed. Nothing should be printed after this line!");
        console.log("------------------------------------------------------...-");
      })
      .catch( err => {

      });
  }
  test();
})();

输出:

decisionspace
----------------------------------------------------------
Test completed. Nothing should be printed after this line!
------------------------------------------------------...-
bundle
visualization
feature
feature

1 个答案:

答案 0 :(得分:0)

问题在于:

  Promise.all(promises)
    .then( res => {
      if(recurse && element.children.length > 0) {
        element.children.forEach( child => {
          _add(child, recurse, element.id);
        });
      }
      resolve(element);
    })

_add()会立即返回一个承诺,并将在以后异步完成其任务。完成forEach循环后,_add()尚未完成任务。

如果要确保按顺序添加元素,等待上一个_add()已完成的任务是调用下一个之前的任务,您可以写:

  Promise.all(promises)
    .then( res => {
      var taskQueue = Promise.resolve();
      if(recurse && element.children.length > 0) {
        element.children.forEach( child => {
          taskQueue = taskQueue.then(() => _add(child, recurse, element.id));
        });
      }
      taskQueue.then(() => resolve(element));
    })

如果您不关心元素是否按顺序添加,您可以写:

  Promise.all(promises)
    .then( res => {
      var tasks = [];
      if(recurse && element.children.length > 0) {
        element.children.forEach( child => {
          tasks.push(_add(child, recurse, element.id));
        });
      }
      Promise.all(tasks).then(() => resolve(element));
    })