【问题标题】:How do promise chains start and finish承诺链如何开始和结束
【发布时间】:2017-02-12 22:20:45
【问题描述】:

我对我遇到的各种文档中的排序如何工作感到有些困惑。比如我见过这样的事情

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());

我真正不明白的是,如果您在主代码中做一些需要大量时间的事情,这是否意味着其中一个承诺可以在主代码实际开始设置之前完成上。然后。

你能不能总是在 promise 上使用 .then/.catch ,如果它已经完成,它会从它到达的地方继续,所以它在概念上看起来像一个大链,会在某些时候运行到完成不确定点?如果是这样,那是不是意味着每次你创建一个 Promise 链时它都会一直挂着?

【问题讨论】:

  • @jfriend00 不需要。
  • 如果dosomething()finalthing() 返回一个要调用的函数,那么也许该代码是正确的......但是,我怀疑这里的情况
  • @jfriend00 正是 Jaromanda X 所说的。 see this question
  • 请解释为什么这不清楚,我会尽力澄清(尽管 jfriend00 似乎已经足够清楚了)

标签: javascript promise es6-promise


【解决方案1】:

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

是的,你总是可以在 promise 上使用 .then/.catch。当你拨打p.then()时,有三种可能。

  1. promise p 仍处于待处理状态(尚未完成或拒绝)。如果是这种情况,那么您传递给 .then() 的函数引用将注册为该承诺的侦听器。因此,当未来的状态转换发生在 Promise 上时(从挂起 => 已完成或从挂起 => 拒绝),将调用适当的注册监听器。

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

  3. promise p 已被拒绝。如果是这种情况,那么调用.then(f1, f2) 将安排f2 在下一次滴答时被调用(在当前的Javascript 完成执行后),它将传递保存的拒绝原因。

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

同样的逻辑也适用于.catch(),只是它只对上述情况 1 和 3 感兴趣。

这里是

如果是这样,那是不是意味着每次你创建一个 Promise 链时它都会一直挂着?

Promise 只是对象,就像 Javascript 中的任何其他对象一样。只有当其他代码仍然对它们有一些实时引用时,它们才会徘徊。一旦不再有任何方法可以访问该 promise 对象,它们就会像 Javascript 中的任何其他对象一样有资格进行垃圾回收。

所以,如果你这样做:

var p = somePromiseReturningFunction();

p.then(f1).then(f2).then(f3);

然后,p 将一直存在,直到 somePromiseReturningFunction() 完成对它返回的承诺的任何引用(通常,尽管并不总是,这发生在承诺最终被履行或拒绝时)以及当变量 @987654337 @ 超出范围。如果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().catch() 处理程序。

我真正不明白的是,如果您在主代码中做一些需要大量时间的事情,这是否意味着其中一个承诺可以在主代码实际开始设置之前完成.then.

首先,我在回答开头的原始解释应该解释说,在已经解决的承诺上调用 .then() 非常好,所以这根本不是问题。它只会在事件循环的下一个滴答声中安排动作。

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

【讨论】:

  • @Bergi - 我想你在技术上是正确的,但请向我解释为什么拒绝承诺会将其置于rejected 状态,但解决承诺会将其置于fulfilled 状态(不是处于已解决状态)。这似乎是一个相当不幸的术语选择,确实让人们感到困惑,包括像我这样相对有经验的人,甚至更没有经验的人。为什么我们不能将承诺称为待处理、已解决或已拒绝?
  • @Bergi - 注意,promise 构造函数的 ES6 规范说,传递给构造函数的执行器是一个接受两个参数的函数,一个是解析函数,一个是拒绝函数。因此,ES6 规范将动词称为“解决”和“拒绝”承诺。这就是我们开始为这些操作使用这些名称的地方。因此,resolve 函数将状态更改为已完成而不是已解决是没有意义的。我总是对此感到困惑。
  • 履行承诺会将其置于履行状态。 “解决”并不总是履行承诺,您也可以将其解决为另一个(可能被拒绝的)承诺。另请参阅here
  • @Bergi - 答案中的术语已更新,但我不相信这会使 OP 更容易理解和遵循答案。我看过你的其他参考资料,我得到了细微的区别,但我仍然认为这是诺言的创造者所做的不幸的混乱,它使事情变得比他们需要解释和谈论的更困难。
  • @Bergi - 实际上,this answer of yours 对我的帮助更大。这基本上说实际上有四种状态:待处理、拒绝、已履行,正如我们一直在讨论的,第四个是已解决,这意味着这个承诺正在跟踪其他一些承诺,它最终将根据承诺发生的情况进入履行或拒绝它正在跟踪。这不正确吗?现在,如果我能真正记住这一点(我已经尝试了多年,我认为这说明了一些不幸的术语被使用的方式)。
【解决方案2】:

我真正不明白的是,如果您在主代码中做一些需要大量时间的事情,这是否意味着其中一个承诺可以在主代码实际开始设置之前完成.then.

由于 JavaScript 的常见实现从不并行运行它,所以不会。尽管如此,

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

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

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

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

如果是这样,那是不是意味着每次你创建一个 Promise 链时它都会一直挂着?

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

【讨论】:

    猜你喜欢
    • 2018-02-27
    • 1970-01-01
    • 1970-01-01
    • 2015-01-06
    • 2020-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-04
    相关资源
    最近更新 更多