【问题标题】:Implement blocking control flow in NodeJS在 NodeJS 中实现阻塞控制流
【发布时间】:2016-06-10 20:21:40
【问题描述】:

我正在尝试创建一个阻塞控制流,但已经走到了死胡同。 这个想法是一个请求进来并流经许多处理函数。每个功能都会做一些事情并决定现在是否满足请求。如果是,则处理停止,如果不是,则调用下一个处理程序。 每个处理程序可能会或可能不会异步执行某些操作(例如获取/写入数据)。如果是这种情况,则以下处理程序依赖于在启动之前存在的数据。

到目前为止,我有两个想法,都不太符合这些要求: 1) 所有处理程序都是函数,它们被推入一个数组并使用 some 进行迭代。如果处理程序希望停止控制流,它只需要返回true。 使用这种方法,我不能让任何处理程序调用异步函数。

2) 所有处理程序都是链接的承诺。这对我来说似乎是一个更好的主意,但我不知道如何最好地处理停止控制流。我有两个想法: - 一旦处理程序决定中断流,保留一个设置为true 的变量。在每个处理程序中检查此值并在需要时立即解决。这意味着很多重复的代码。 - 如果不希望继续正常流程,则拒绝承诺,并继续catch。然而,使用错误来控制流程的想法让我很痛苦——这也意味着我必须处理由于实际错误而调用 catch 的情况。

我的感觉告诉我必须有另一种方法,但我想不通。

【问题讨论】:

  • 一些 Promise 实现,如bluebird,提供promise cancellation
  • 基本上,您是在要求寻找任一单子。哪个承诺或多或少是。我推荐this talk,尽管它是专门针对节点的。对于一个实际的答案,我认为使用拒绝来停止流动是可以的。没有什么表明它意味着“错误”,这只是一个语义约定
  • 编辑限制后的错字:上面应该专门说 not 节点...

标签: javascript node.js promise


【解决方案1】:

您可以根据需要使用 ES6 生成器来生成 Promise。每次 Promise 解决时,它都可以检查停止值,如果为 false(即继续到下一个处理程序),则从迭代器请求下一个。

function* promiseHandlers() {
  const handlers = [
    promiseHandler1,
    promiseHandler2,
    promiseHandler3,
    promiseHandler4,
    promiseHandler5
  ];

  for (const handler of handlers) {
    yield handler();
  }
}

function run(iter) {
  const i = iter.next();
  if (i.done) {
    return Promise.reject(new Error("end of iterator reached"));
  }
  return Promise.resolve(i.value).then(result => result || run(iter));
}

run(promiseHandlers());

这实际上并不是链接。相反,它会按顺序执行每个 promiseHandler,但这些结果不会传递给下一个。我认为这对于 OP 想要的东西是正确的,特别是因为真实的结果结束了迭​​代(即中断)。

【讨论】:

  • 确实遗漏了一些东西 :-) 我希望你不介意编辑。
  • 这个递归函数确实比我的方法好一点,因为在执行之前不构建整个“链”会更高效。这样它也适用于开放式迭代 - 它更像是一个 while 循环而不是 for of 循环 - 很好地描绘了可能是无限的生成器。
  • 不要滚动你自己的协程!你错过了一堆案例。介意我让你的run 更“合规”吗?
  • @JoseHermosillaRodrigo:Promise.reject 是在迭代器用完处理程序时遇到的,当我们在没有找到一个符合真实值的承诺时到达它的终点。
  • result => result || run(iter) 等价于function (result) {return result || run(iter)}。阅读 ES6 箭头函数语法。使用箭头函数将帮助您大大减少实现相同功能所需的输入量,并且它们还可以帮助您编写更易于阅读的代码。
【解决方案2】:

不要链接承诺处理程序,嵌套它们。您可以通过一组函数以编程方式执行此操作:

var handlers = […]; // functions that can return promises

var run = handlers.reduceRight(function(next, handler) {
    return function() {
        return Promise.resolve(someArg).then(handler).then(function(result) {
            if (result) // true, whatever
                return result;
            else
                return next();
        });
    };
}, function end() {
    return Promise.reject(new Error("no handler matched"));
});

run().then(function(result) {
    // the result from the first handler that returned anything
}, function(err) {
    // some handler threw an exception, or there was no handler
});

【讨论】:

  • 我承认我很难理解这段代码。到目前为止,我无法让处理程序失败,即使显式返回 false,并且在调用每个处理程序后,也会调用 end() 函数,从而引发错误。 if语句中的结果和函数参数(res)一样吗?
  • @PhilippFlenker:啊,是的,参数名称中的错字。当处理程序返回true 时它应该停止,当它返回false 时它应该继续next(字面意思),当它抛出时它会直接进入catch 回调。
  • 好的,看来我在调整您的解决方案时犯了一个错误。我希望处理程序接受一个参数(在所有情况下都相同)。它似乎在没有参数的情况下工作。您对如何使其与参数(如 handler(param) )一起工作有什么建议吗?
  • 我检查了您的解决方案,它解决了我的问题,谢谢!现在我要弄清楚的是参数的东西......
  • @PhilippFlenker:我已将someArg 放入我的sn-p 中,将发送到handler
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-05
  • 1970-01-01
  • 2019-03-28
  • 1970-01-01
  • 2015-05-24
  • 2016-08-26
相关资源
最近更新 更多