【问题标题】:What's the purpose of the promise object that gets returned by the .then() method?.then() 方法返回的 promise 对象的目的是什么?
【发布时间】:2017-07-13 09:07:45
【问题描述】:

我在多个网站上读到,promise.prototype 中的 .then() 方法返回一个 promise。不幸的是,没有消息来源描述这背后的原因。

then() 方法返回一个 Promise。它最多需要两个参数:Promise 成功和失败情况的回调函数。 - developer.mozilla.com

为什么/什么时候有人需要这个返回的 promise 对象,这个 promise 对象与原始对象有什么关系。

非常感谢您的帮助。

【问题讨论】:

  • 我推荐凯尔辛普森的书You Don't Know JS: Async & Performance。它详细介绍了 Promise 的好处,除了链式等显而易见的好处。

标签: javascript asynchronous promise es6-promise


【解决方案1】:

promise 是异步执行的,你永远不知道 then() 什么时候会被执行。

promise 可以返回一个promise,这允许您在单行代码中链接异步事件处理。

Mozilla 给出的示例代码:

doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

它避免了“厄运金字塔”:

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

【讨论】:

  • 嘿,它们究竟是如何异步执行的?感谢您的精彩回答。
  • Javascript 在其单线程中以异步方式执行其代码:部分代码被执行,而其他部分可能被阻塞,例如,等待对网络请求的响应。执行器在收到事件时切换,并在处理事件时恢复执行其他代码部分。
  • 没有 Javascript 执行器可以在收到事件时切换然后恢复,并且异步 I/O 也没有阻塞。这根本不是 Javascript 中事件驱动逻辑的工作方式。事件不会中断其他正在运行的代码。相反,一段正在运行的 Javascript 会一直运行,直到它完成执行。然后,解释器检查事件队列以查看是否有另一个事件要运行,如果有,它会运行下一个。如果没有任何东西在运行并且一个事件被添加到事件队列中(通过一些本机代码),那么该事件将被运行。
  • 是的,我简化了很多,已经晚了...谢谢你让它更清楚。
【解决方案2】:

.then() 返回一个承诺的事实有三个主要方面。

第一个是您可以像这样链接操作:

a().then(b).then(c).then(d)

因为.then() 返回一个新的promise,所以在新的promise 解决之前,下面的.then() 处理程序不会被执行。如果 b 和 c 是同步的,那么新的 Promise 将在它们返回时解析,并且当第一个 b 完成时链将继续,然后当 c 完成时。

第二个是新的承诺会受到.then()处理程序返回的影响。这允许 b、c 和 d 是异步操作,它们本身返回 Promise,并且链将被适当地排序。所以,想象一下 b 和 c 都自己返回 Promise。

首先你得到a() 返回一个承诺。当它解决时,它的.then() 处理程序被调用。然后将运行b。如果b() 也是一个异步操作并且它返回一个新的promise,那么a.then(b) 返回的所有其他.then() 处理程序链接到的promise 将不会被解析,直到b 返回的新promise 被解析。这允许.then() 处理程序将新的异步项插入到链中。这是链接承诺的一个非常重要的方面。 .then() 处理程序可以将它们自己的异步操作插入到链中,甚至可以根据先前的结果或当前状态有条件地执行此操作。

如果a().then(b) 刚刚返回了与a() 返回的相同的承诺,那么所有后续的.then() 处理程序将无法“等待”b() 返回的承诺,因为它们将被链接到a() 承诺,它已经解决了。正是这个新 Promise 的返回允许 .then() 处理程序中的函数影响后续链,因为新的 Promise 受 .then() 处理程序返回的内容影响。

第三个方面是.then() 处理程序的返回值可以影响新承诺的解析值,这就是传递给链中下一个.then() 处理程序的值。如果a().then(b) 刚刚返回了与a() 返回的相同的承诺,那么所有后续的.then() 处理程序只会看到来自a() 的相同解析值,因为该解析值已经在a() 解析时设置,它在@ 之前987654350@ 调用了它的.then() 处理程序。这些后续的.then() 处理程序将无法从.then() 处理程序内的代码继承新的解析值。


让我们看一个具体的场景。我将使用延迟方法作为一个简单的函数示例,该函数返回一个将来会解析的 Promise。

function delay(t, val) {
    return new Promise(resolve => {
        setTimeout(() => resolve(val), t);
    });
}

然后,定义几个不同的异步函数:

function a(val) {
    return delay(100, val + 1);
}

function b(val) {
    return delay(50, val + 10);
}

function c(val) {
    return val * 100;
}

现在,将它们全部串起来:

a(100).then(b).then(c).then(val => {
    console.log("all done: ", val);
});

这是一步一步发生的:

  1. a(100) 被调用。这会调用delay(它设置一个计时器)并返回一个我将调用a1_promise 的promise,只是为了在此处进行描述。

  2. 然后,因为我们在做a(100).then(b),所以我们从a(100)(即a1_promise)获取返回值并调用a1_promise.then(b)。这会将b 函数存储为.then() 处理函数,以便在将来某个时间解决a1_promise 时调用(不是现在)。然后返回一个新的承诺,我将称之为a2_promise

  3. 然后,因为我们正在执行a(100).then(b).then(c),所以我们从a(100).then(b)(即a2_promise)获取返回值并调用a2_promise.then(c)。它将c 函数存储为.then() 处理函数,以便在将来某个时间解决a2_promise 时调用(不是现在)。然后返回一个新的承诺,我将称之为a3_promise

  4. 然后,因为我们在做a(100).then(b).then(c).then(...),所以我们从a(100).then(b),then(c)(即a3_promise)获取返回值并调用a3_promise.then(c)。它将我们最后一个匿名函数存储为 .then() 处理函数,以便在将来某个时间解决 a3_promise 时调用(不是现在)。然后返回一个新的承诺,我将称之为a4_promise(没有人使用)。

  5. 现在我们完成了同步执行。请注意,a().then(b).then(c).then(...) 都是同步执行的。所有三个.then() 方法都已经在所有不同的promise 上调用过。但是,由于此处创建的所有承诺都尚未解决,因此实际上还没有调用任何 .then() 处理程序。它们都刚刚被存储起来,以便将来在 promise 解决时调用。

  6. 现在经过一段时间,a() 内部创建的计时器触发并解析a1_promise。然后触发a1_promise 调用它拥有的任何.then() 处理程序并将a1_promise 的解析值传递给它,在这种情况下将是100 + 1101。由于a1_promise 上只有一个.then() 处理程序并且它是b() 函数,因此它现在将调用b(101)。执行它只会返回一个新的承诺,b() 创建并返回。我们将把这个新的承诺称为b_promise。在a1_promise() 内部,它知道它在之前调用a1_promise.then() 时创建了a2_promise(),因此它知道当它执行存储的.then() 处理程序以及.then() 处理程序执行并返回一个新的promise 时,它​​持有关闭解决 a2_promise that it created until thatb_promiseis resolved. In this way, you can see that further execution of the chain is now controlled by theb_promise, thus the code executing inb()and the promise is returned are inserted into thea().then().then().then()chain holding off future.then()handlers until theb_promise` 已解决.

  7. 现在又过了一段时间,在 b() 内部创建的计时器触发并使用新修改的 101 + 10 值解析 b1_promise,即 111。这告诉a2_promise,它现在可以使用该值进行解析。

  8. 然后a2_promise 可以调用它的.then() 处理程序并可以执行c(111),就像在步骤6 中返回的c_promise 一样,它还没有解决。

  9. 一段时间过去了,c_promise 解析为 111 * 100 which is11,100. That tells thea3_promise`,现在它可以使用该值解析。

  10. 然后 a3_promise 可以调用它的 .then() 处理程序,这是我们在链末尾的箭头函数,我们得到一个 console.log() 显示 11000 作为最终值。

【讨论】:

    【解决方案3】:

    这是 Promise 的重要部分。

    例如,您可以链接很多方法,其中一个取决于上面的方法结果(在本例中为 Promisse.resolve)。赞this

    【讨论】:

      猜你喜欢
      • 2018-07-07
      • 1970-01-01
      • 2020-04-16
      • 2013-02-10
      • 1970-01-01
      • 2011-04-12
      • 1970-01-01
      • 2021-03-17
      • 2017-11-11
      相关资源
      最近更新 更多