【问题标题】:Executing a queue of handlers that return a promise执行返回承诺的处理程序队列
【发布时间】:2014-08-24 03:18:57
【问题描述】:

我有一个处理程序队列,我需要按插入顺序执行,然后等待每个处理程序返回一个已解决的承诺,然后再执行下一个。

我决定使用递归函数将处理程序移出队列然后执行它。当处理程序解决其承诺时,该函数将递归调用自身,直到队列为空。

var executeHandler = function(handlers, instance){
    var handler, cb = function(){
        return executeHandler(handlers, instance);
    };

    if(handlers.length){
        handler = handlers.shift();
        return handler($q, instance).then(cb);
    }
};

这是一个适当的解决方案。但是我不确定这是一个理想的解决方案。单步执行此功能时,很难遵循。

有没有更优化的方法来实现这一点?更重要的是更容易掌握的解决方案?

【问题讨论】:

  • 如果我理解正确的话,也许你想看看$q.all
  • 我看到的方法是使用then()返回的promise来链接promise,你把它们放入数组有什么原因吗?你能再解释一下吗?
  • @elclanrs $q.all 是我最初的结构。然而,它只是解决了一系列被解决或拒绝的承诺。我需要每个函数(即处理程序)在调用下一个之前等待前一个解决。
  • 异步的东西本来就很难单步执行,而且我还没有看到任何调试工具或编码风格真正使它变得更容易。您最终只需要设置断点,因为您无法逐行遍历异步内容。就其本质而言,它完成了执行现在需要完成的事情,然后稍后调用一些回调,这不适合逐步调试。你所拥有的对我来说似乎是合理的。您也可以使用.reduce() 立即将它们链接在一起,但这对调试没有任何帮助。
  • 我猜.reduce 模式并不比递归解决方案更容易逐步完成,但它正在迅速成为数组异步迭代的事实上的模式,因此(在时间上)更容易识别。

标签: javascript angularjs recursion promise


【解决方案1】:

好吧,就像jfriendRoamer 建议的那样——你不必实际实现递归,因为promise 已经链接了。让我们回顾一下:

  • promise 表示操作的最终值。
  • .then 代表代码中的分号或换行符,它是对流控制和排序本身概念的抽象。它实际上非常接近于一个 monad bind
  • 因此,当我们将流控制转换为 Promise 时,我们将分号替换为 thens。

让我们从同步版本开始

function each(handlers, instance){
    // here the function flow control starts
    handlers.forEach(function(handler){ // iterate all handlers
         handler(instance); // call the handler
    }); // in the promises version we'll iterate in advance since it's simpler
    return; // when the function returns it's done
}

现在,我们如何将其转换为承诺代码?

  • 流程控制开始由一个空的已解决承诺表示:$q.when
  • forEach 循环是相同的,但我们将同步迭代数组和链函数执行,因为在这种情况下更简单。
  • handler() 将是“等待前一个处理程序完成然后运行这个处理程序”。
  • return 将返回链式承诺,以便我们知道何时完成。

这应该类似于:

function each(handlers, instance){
    var p = $q.when();
    handlers.forEach(function(handler){ 
        p = p.then(function(){ return handler(instance); });
    });
    return p;
}

这里的优点是流程是循环而不是递归,这对某些人来说更容易遵循,基本上我们没有实现我们自己的“下一个”逻辑,而是将这个责任委托给了 Promise。以下是reduce 中 jfriend 和 roamer 的含义:

function each(handlers, instance){
    return handlers.reduce(function(p, next){ 
        return p.then(function(){ return next(instance); });
    }, $q.when());
}

这种事情(将一个数组聚合成一个值)正是 reduce 的用途,这就是它如此自然地适合的原因。

虽然 for 循环解决方案可以说使用调试器并不容易单步执行,但我认为它比第一个解决方案的显式递归和簿记要容易得多。

【讨论】:

  • 我很欣赏详尽而简洁的回答。我想使用减少模式。但是有没有办法“打破”减少迭代?
  • @JonathanPalumbo 没有break,您可以在then 中抛出错误并捕获拒绝(因为承诺是安全的)。或者您可以返回一个虚拟对象并迭代集合而不实际附加到它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-24
  • 2021-07-22
  • 2018-03-06
  • 2020-12-20
  • 1970-01-01
  • 2017-12-26
相关资源
最近更新 更多