为什么在回调中取消蓝鸟承诺停止setInterval?

时间:2015-03-02 13:40:30

标签: javascript node.js coffeescript promise bluebird

Promise = require 'bluebird'

cb = ->
  console.log 'callback!'
  p = Promise.resolve(5)
    .cancellable()
    .tap -> p.cancel()

setInterval(cb, 100)

cb函数只调用一次。注释.tap -> p.cancel()允许它重复运行。添加try块无济于事。也许这是显而易见的事情,但我做了一些研究而无法找到解释。

1 个答案:

答案 0 :(得分:4)

似乎从p.cancel()处理程序返回tap的值的行为导致bluebird进入某种无限循环。你永远不会看到第二个'callback!'因为执行上下文在100毫秒之前停留在这个循环中。

我还远远没有理解这里发挥的所有因素(见下文),但看起来这可以通过不返回p.cancel()来解决:

Promise = require 'bluebird'

cb = ->
  console.log 'callback!'
  p = Promise.resolve(5)
    .cancellable()
    .tap -> 
        p.cancel()
        null

setInterval(cb, 100)

编辑:好的,看了几次来源并解开了我的大脑几次后,我认为可以归结为:

执行陷入无限循环,.cancel()试图爬上承诺链:

while ((parent = promiseToReject._cancellationParent) !== undefined &&
    parent.isCancellable()) {
    promiseToReject = parent;
}

重点如下:

  • p.cancel()返回p
  • .tap()返回一个承诺,只要从其处理程序返回的promise返回(如果它返回一个promise),它就会解析
  • p.tap()返回的承诺

换句话说,p是在p结算后将解决的承诺。它是承诺链中的自己的祖先(至少,我认为是这样)。

.cancel()试图爬上承诺链以找到可取消的承诺时,它会发生在这种乱伦的关系中,并开始永远地进入圈内。

最后,CoffeeScript渴望将几乎所有内容都变成return语句,这是一个不幸的结果。但我想,Bluebird可以通过某种方式检测到promise链中的循环,并防止在这里发生无限循环。

我已经在bluebird GitHub存储库中为此提交了issue,但正如随后的讨论所揭示的那样,.cancel()的这种使用无论如何都没有任何意义。