承诺链如何开始和结束

时间:2017-02-12 22:20:45

标签: javascript promise es6-promise

我对我所遇到的各种文件中的测序工作方式感到有些困惑。例如,我见过这种事情

let p = Promise.resolve();
for (let x in something)
{
    /* do some hairy time consuming code */
    p = p.then(dosomething(x));
}
return p.then(finalthing()).catch(pretendnobadthing());

我真正不理解的是,如果你在主代码中花费了大量时间,那么这并不意味着其中一个承诺可以在主代码实际获得之前完成转到设置.then。

你能否总是在承诺上使用.then / .catch,如果它已经完成,它将继续从它到达的地方继续,所以它在概念上看起来像一个将完成的大链在一些不确定的点?如果是这样的话,这并不意味着每次你创建一个承诺链,它会永远挂起来吗?

2 个答案:

答案 0 :(得分:2)

  

你能否总是在承诺上使用.then / .catch,如果它已经完成,它将继续从它到达的地方继续,所以它在概念上看起来像一个大链条,将在某些情况下运行完成不确定点?

是的,你总是可以在承诺上使用.then / .catch。当你致电p.then()时,有三种可能性。

  1. 承诺p仍未决定(尚未履行或拒绝)。如果是这种情况,那么传递给.then()的函数引用将被注册为该承诺的侦听器。因此,当未来状态转换发生在承诺上时(要么从pending =>履行,要么从pending =>拒绝),那么将调用相应的已注册侦听器。

  2. 承诺p已经履行。如果是这种情况,那么调用.then(f1, f2)将调度f1在下一个滴答时调用(在当前Javascript完成执行之后)并且将传递保存的已解析值。

  3. 承诺p已被拒绝。如果是这种情况,那么调用.then(f1, f2)将安排f2在下一个tick(当前Javascript完成执行之后)被调用,并且它将被传递保存的拒绝原因。

  4. 因此,对已经履行或拒绝的承诺调用.then()是完全安全的。适当的监听器将被安排在下一个时钟运行。

    同样的逻辑适用于.catch(),除了它只对上面的案例1和3感兴趣。

    这是

      

    如果是这样,这并不意味着每次你创建一个承诺链,它都会永远存在吗?

    Promise就像Javascript中的任何其他对象一样。只有当其他代码仍有一些实时引用时,它们才会挂起。一旦没有任何方法可以到达该promise对象,它们就像符合Javascript中的任何其他对象一样有资格进行垃圾收集。

    所以,如果你这样做:

    var p = somePromiseReturningFunction();
    
    p.then(f1).then(f2).then(f3);
    

    然后,p将保持不变,直到somePromiseReturningFunction()完成对其返回的承诺的任何引用(通常,但并非总是如此,这在承诺最终被履行或拒绝时发生)以及何时变量p超出范围。如果p永远不会超出范围(例如它是全局范围或其他持久范围),那么它将永远保留(就像任何其他Javascript对象一样)。

    你的问题中存在一些误解,所以让我尝试解决这些问题。

    您正在使用可能不正确的构造p = p.then(dosomething(x));。您需要传递.then()函数引用。因此,除非您希望doSomething(x)立即执行,并且它还返回另一个您想要调用的函数,然后.then()处理程序(这似乎不太可能),那么这不是正确的构造。你可能想要:

    p = p.then(result => dosomething(x));
    

    或ES5语法:

    p = p.then(function(result) {
        return dosomething(x)
    });
    

    你在这方面也表现出同样的问题:

    return p.then(finalthing()).catch(pretendnobadthing());
    

    应该是:

    return p.then(finalthing).catch(pretendnobadthing);
    

    请记住,当您使用f()时,这意味着立即执行f。当您传递f时,它传递一个函数引用,您传递给它的基础函数/方法随后可以在选择时调用,这是.then()和{{{ 1}}处理程序。

      

    我真正不明白的是,如果你在主代码中花费了大量时间,那么这并不意味着其中一个承诺可以在主代码实际到达设置之前完成.then。

    首先,我在答案开头的原始解释应该解释,在已经解决的承诺上调用.catch()完全没问题,所以这根本不是问题。它只会在事件循环的下一个刻度上安排操作。

    但是,这里的情况甚至不是这样,因为浏览器中的Javascript和node.js是单线程的,所以当你的长时间运行的代码运行时,那个承诺(以前是谁的异步动作)还不能得到解决。虽然可以完成基础异步操作,并且事件可能位于将触发将解析promise的回调的Javascript事件队列中,但事件队列中的该事件将不会被处理,直到正在执行的当前Javascript片段完成并将控制权返回给系统。

答案 1 :(得分:0)

  

我真正不明白的是,如果你在主代码中花费了大量时间,那么这并不意味着其中一个承诺可以在主代码实际到达设置之前完成.then。

由于JavaScript的常见实现从不并行运行,否则。尽管如此,

  

你能否总是在承诺上使用.then / .catch,如果它已经完成,它将继续从它到达的地方继续,所以它在概念上看起来像一个大链条,将在某些情况下运行完成不确定点?

是的!这是承诺的一大优势。以此为例:

var promise = Promise.resolve(5); // already resolved here

setTimeout(function () {
    promise.then(function (x) {
        console.log(x); // still logs 5
    });
}, 1000);
  

如果是这样,这并不意味着每次你创建一个承诺链,它都会永远存在吗?

直到它被解决,是的,但如果承诺已经解决并且没有办法再引用它,它可以像任何其他对象一样被处理掉。