【问题标题】:In ES6 JavaScript, why is the promise returned by the fulfillment handler of then() not the same as the promise returned by then()?在 ES6 JavaScript 中,为什么 then() 的实现处理程序返回的 Promise 与 then() 返回的 Promise 不一样?
【发布时间】:2020-04-16 18:14:38
【问题描述】:

我有一些方块在页面上滑动,以链式承诺方式: https://jsfiddle.net/u4x0qwfo/3

代码是:

new Promise(function(resolve, reject) {
  $("#shape").css({
    top: 100
  });
  setTimeout(function() {
    resolve();
  }, 1000);
}).then(function() {
  return new Promise(function(resolve, reject) {
    $("#shape2").css({
      top: 100
    });
    setTimeout(function() {
      resolve();
    }, 1000);
  });
}).then(function() {
  return new Promise(function(resolve, reject) {
    $("#shape3").css({
      top: 100
    });
    setTimeout(function() {
      resolve();
    }, 1000);
  });
}).then(function() {
  return new Promise(function(resolve, reject) {
    $("#shape4").css({
      top: 100
    });
    setTimeout(function() {
      resolve();
    }, 1000);
  });
});

(代码在此处的 sn-p 内运行不佳:此处第一个方块最初已经向下滑动)。

所以要查看履行处理程序返回什么承诺,以及.then() 返回什么承诺,我有https://jsfiddle.net/u4x0qwfo/10/

代码是:

let foobar;

let lala = new Promise(function(resolve, reject) {
  $("#shape").css({
    // ...
}).then(function() {
  foobar =  new Promise(function(resolve, reject) {
    // ...
  return foobar;
});

lala.then(function() {
  console.log("checking:", lala, foobar, lala === foobar);
  return new Promise(function(resolve, reject) {

在调试控制台中,我们可以看到promise 是不同的。但是为什么它们必须不同呢?

其实在the docs of .then() return value里面,据说是:

[如果.then()] 返回另一个待处理的promise 对象,then 返回的promise 的解析/拒绝将在处理程序返回的promise 的解析/拒绝之后。此外,then 返回的 promise 的值将与 handler 返回的 promise 的值相同。

这表明这两个 promise 是不同的(一个由履行处理程序返回,一个由 .then() 返回)。 (我在the ES6 specs 中找不到描述)。问题是为什么?他们不能是同一个承诺吗?

第二部分说:

此外,then 返回的 promise 的值将与 handler 返回的 promise 的值相同。

我最初认为它的意思是“那时返回的承诺将与处理程序返回的承诺相同”,但只是发现它实际上意味着:“then 返回的承诺的解析值将是与处理程序返回的承诺的解析值相同”。 (这是描述它的正确方式吗?)。

在 Google Chrome 中,两个 Promise 都将显示相同的解析值 123456https://jsfiddle.net/u4x0qwfo/11/

【问题讨论】:

  • "他们不能是同一个承诺吗?" - 不,他们不能。 then() 返回的一个需要在将来创建另一个的回调之前创建,甚至运行。如果有的话 - 承诺可能会拒绝。

标签: javascript es6-promise


【解决方案1】:

这是因为这些承诺是在不同的时间创建的:

new Promise(....).then() 返回一个立即可用的承诺(如lala),而传递给then 方法的回调将仅在new Promise(....) 解析时执行,即可能更晚。

then回调最终执行时,它可以完全控制它返回的内容,所以如果它决定返回一个新的承诺,那么它怎么可能是前一段时间已经通过调用创建的承诺.then()?

或者,换一种说法,如果then 回调返回一个新的promise,为什么JavaScript 会否决这个创建,并在那里注入已经存在的promise?那是不可取的。

在您的代码中,如果您在任何 then 回调中检查 lala,您会注意到它已定义(因为到那时所有非回调代码都已完成),而分配给 foobar仍有待执行。它们不能相同。

【讨论】:

  • 所以当foobar被返回时,一些机制接受这个返回值,并看到它是一个承诺,并立即调用returnedValue.then(fn1, fn2),其中fn1是实现处理程序,它解析@ 987654336@...
  • 您可以在第 2.3 节的 Promises/A+ 参考中找到所发生情况的规范。在该文本中,“thenable x”是您的foobarpromise 是您的lala。您还可以查看库如何实现该特定规范。我已经发布了我自己的实现here
【解决方案2】:

非常简短的回答 then() 返回一个启用方法链接的承诺,如果你告诉你的履行处理程序返回一个承诺,一个等效的承诺将提供给下一个 then()。这意味着如果你创建一个 Promise 并返回它,.then() 将访问它。

下面的证明

const promise1 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve("resolve promise1");
    }, 1000);
});

const promise2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve("resolve promise2");
    }, 2000);
});

promise1.then(() => { return promise2 }).then(value => console.log(value))
//this will return resolve promise 2 after 2 seconds

这意味着 .then() 调用将使用 Promise1 的实现处理程序返回的 Promise 的解析值,在本例中为 Promise2。

对于您的情况 foobar !== lala,因为在这种情况下 lala 是链中的“第一个承诺”,而 foobar 是第 4 个或第 5 个,但是当您访问 lala.then() 时,您正在访问 foobar 承诺,但因为它不包含任何您不会注意到的解析值。

用一个值解析 foobar 并在 lala.then(res=>res....) 中访问它

【讨论】:

  • 很高兴您解释了 OP 的代码,但我很确定 OP 知道这一点。重写他们的代码也很好,但我很确定 OP 也没有要求这样做。您在哪里尝试回答他们的问题?
  • 我重写了代码,以便更好地解释如果其他人绊倒会发生​​什么
  • 但是,你没有回答这个问题。这就像您发布答案时的要求。
  • 感谢您指出这一点。我居然得出了不同的结论,看来他们其实是一样的。
  • 在你更新之后,这个语句是错误的:"then() 不会返回一个 promise,除了..."then() 总是返回一个承诺。这实际上增加了混乱。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-03
  • 1970-01-01
相关资源
最近更新 更多