嵌套JavaScript和异步函数/回调/ Promises的模式

时间:2017-08-23 07:29:34

标签: javascript promise es6-promise

我偶尔遇到这种问题,目前我最终不得不重构大量代码来解决这个问题。我的问题是,是否有一个更简单的模式。

我得到了这个问题的印象,但我还没有找到答案。例如,SO显示this Q&A,但它显然不是同一个问题

短伪代码版本是这样的。我从命令式编程开始。

/**
 * node: some node in a tree of nodes
 * leave: an array on to which leaves will be pushed in depth first order
*/
function extractLeaves(node, leaves) {
   switch (node.type) {
     case 'leaf':
       leaves.push(node);
       break;
     case 'special':
       extractLeavesFromSpecial(node, leaves);
       break;
     default:
       extractLeavesFromChildren(node, leaves);
       break;
   }
}

function extractLeavesFromChildren(node, leaves) {
   node.children.forEach((child) => extractLeaves(child, leaves); 
}

function extractLeavesFromSpecial(node, leaves) {
   // skip leaves if they're this node is not special
   if (isSomethingIsReallySpecial(node)) {
     extractLeavesFromChildren(node, leaves);
   }
}

const leaves = [];
extractLeavesFromChildren(root, leaves);

你可以看到它的命令性和递归性并且它正在工作

现在我突然想要使用一些在递归中使用回调的库。

     case 'special':
       extractLeavesFromSpecial(node, leaves);
       break;
     case 'newSpecial':
       extractLeavesFromNewSpecial(node, leaves);
       break;


function extractLeavesFromNewSpecial(node, leaves) {
   someLibraryFunctionThatsAsync(node, callback);  // UGH!!!
   ....

解决方案似乎是承诺,这很好,我可以做到。

我的问题是,我是否必须重构每个功能?

AFAICT当我遇到异步所需的解决方案时,我突然不得不重新编写所有函数以使用promises all,因为其中只有一个最终是异步的。

function extractLeaves(node) {
  return new Promise(resolve  => {
    switch (node.type) {
      case 'leaf':
        resolve([node]);
        break;
      case 'special':
        resolve(extractLeavesFromSpecial(node);
        break;
      case 'newSpecial':
        resolve(extractLeavesFromNewSpecial(node));
        break;
      default:
        resolve(extractLeavesFromChildren(node);
        break;
    }
  };
}

function extractLeavesFromChildren(node) {
  return Promise.all(node.children.map(child => {
     return extractLeaves(child, leaves);
  }).then(array => {
     // have to flatten as each element is an array
     // this will also magically filter out empty arrays
     return [].concat.apply([], arrays);
  });
}

function extractLeavesFromSpecial(node) {
   // skip leaves if they're this node is not special
   if (isSomethingIsReallySpecial(node)) {
     return extractLeavesFromChildren(node);
   } else {
     return Promise.resolve([]);   
   }
}

function extractLeavesFromNewSpecial(node) {
   return new Promise((resolve, reject) => {
     someLibraryFunctionThatsAsync(node, (err, result) => {
       if (err) {
         reject(err);
       } else {
         resolve(...);  // not important what this returns to the point
       }
   }
   ....
}

在我的实际代码中,我有大约8个函数,是真的需要什么还是有一些更简单的模式?

注意:在我的情况下,不仅这似乎需要重构一切。它似乎使解决方案稍微复杂一些,因为保持叶子列表平坦需要过滤并检查嵌套路径是否在叶子中结束,而旧方法没有这个问题。这是一条线,但必须考虑问题之前的问题尚不存在。

第二个问题是在命令式方法中,一次处理一棵树很容易,如

lotsOfTrees.forEach(tree => {
  const leaves = [];
  extractLeaves(tree, leaves);
  // do something with leaves.
});

在新的内容虽然每个都是一个承诺,因此没有更多的工作将同时处理(更多的内存),这意味着更多的重构(不是一个大问题,它就像3 -6行代码使得承诺按顺序运行,而不是需要进行大量的重构,以便在我做错事情时提出问题。

从某种意义上说,似乎很明显,因为一个深度函数需要异步才能有效地使所有函数在它之上异步。这意味着所有这些功能都必须重构。我只是仔细检查,我不会错过更简单的事情。

0 个答案:

没有答案