了解 javascript 承诺;堆栈和链接

Understanding javascript promises; stacks and chaining

我 运行 遇到了一些关于 javascript promises 的问题,尤其是关于堆叠链的问题。

任何人都可以向我解释这些不同实现之间的区别(如果有的话!)?

实施 1

var serverSidePromiseChain;
serverSidePromiseChain = async().then(function(response) {
    console.log('1', response);
    return response;
}).then(function(response) {
    console.log('2', response);
    return true;
}).then(function(response) {
    console.log('3', response); // response expected to be 'true'
    return async3();
}).then(function(response) {
    console.log('4', response);
    return async4();
})

实施 2

var serverSidePromiseChain;
serverSidePromiseChain = async().then(function(response) {
    console.log('1', response);
    return response;
});

serverSidePromiseChain.then(function(response) {
    console.log('2', response);
    return true;
})
serverSidePromiseChain.then(function(response) {
    console.log('3', response); // response expected to be 'true'
    return async3();
})
serverSidePromiseChain.then(function(response) {
    console.log('4', response);
    return async4();
})

实施 3

var serverSidePromiseChain;
serverSidePromiseChain = async().then(function(response) {
    console.log('1', response);
    return response;
});

serverSidePromiseChain = serverSidePromiseChain.then(function(response) {
    console.log('2', response);
    return true;
})
serverSidePromiseChain = serverSidePromiseChain.then(function(response) {
    console.log('3', response); // response expected to be 'true'
    return async3();
})
serverSidePromiseChain = serverSidePromiseChain.then(function(response) {
    console.log('4', response);
    return async4();
})

链的一部分 returns 值(第 2 步中的 'true')是否改变了行为?承诺是否要求所有返回值都是异步承诺以保持行为?

您正在说明链接和分支之间的区别。链接将对多个异步操作进行排序,以便一个在前一个完成时开始,您可以链接任意数量的项目以一个接一个地排序。

分支连接多个异步操作,以便在一个触发操作完成时同时进行所有操作。

实现1和3是一样的。他们被束缚着。实现 3 只是使用一个临时变量进行链接,而实现 1 只是直接使用 .then() 中的 return 值。执行没有区别。这些 .then() 处理程序将以串行方式调用。

实现 2 不同。它是分支的,而不是链式的。因为所有后续的 .then() 处理程序都附加到完全相同的 serverSidePromiseChain promise,所以它们都只等待第一个 promise 被解决,然后所有后续的异步操作都同时进行(不是连续的)与其他两个选项一样)。


深入了解它如何与 promises 一起工作可能有助于理解这一点。

当你这样做时(场景 1 和 3):

p.then(...).then(...)

情况如下:

  1. 解释器获取您的 p 变量,在其上找到 .then() 方法并调用它。
  2. .then() 方法仅存储传递给它的回调,然后 return 是一个新的 promise 对象。它此时不调用其回调。这个新的 promise 对象与原始 promise 对象及其存储的回调相关联。直到双方都满意才会解决。
  3. 然后调用新 returned promise 上的第二个 .then() 处理程序。同样,该承诺上的 .then() 处理程序仅存储 .then() 回调,它们尚未执行。
  4. 然后在将来的某个时候,原始承诺 p 会通过其自己的异步操作得到解决。解析后,它会调用它存储的任何 resolve 处理程序。其中一个处理程序将是对上述链中第一个 .then() 处理程序的回调。如果该回调运行完成并且 return 没有任何内容或静态值(例如,return 本身不是一个承诺),那么它将在之后解析它创建的 return 的承诺.then() 被第一次调用。当该承诺得到解决时,它将调用上面第二个 .then() 处理程序安装的解析处理程序,依此类推。

执行此操作时(场景 2):

p.then();
p.then();

此处的一个承诺 p 已存储来自两个 .then() 调用的解析处理程序。当最初的承诺 p 得到解决时,它将调用两个 .then() 处理程序。如果 .then() 处理程序本身包含异步代码和 return 承诺,这两个异步操作将同时进行(类似并行的行为),而不是像场景 1 和场景 3 中那样按顺序进行。

实现 1 和 3 似乎是等效的。

在实现 2 中,最后 3 个 .then() 函数都作用于相同的承诺。 .then() 方法 returns 一个新的承诺。已履行的承诺的价值无法更改。参见 Promises/A+ 2.1.2.2。您在实施 2 中的评论(预期响应为真)表明您不希望如此。不,response 不会为真(除非那是原始承诺的值)。

让我们试试吧。 运行 下面的代码片段可以看出差异:

function async(){ return Promise.resolve("async"); }
function async3(){ return Promise.resolve("async3"); }
function async4(){ return Promise.resolve("async4"); }


function implementation1() {
  logContainer = document.body.appendChild(document.createElement("div"));
  console.log("Implementation 1");
  var serverSidePromiseChain;
  serverSidePromiseChain = async().then(function(response) {
    console.log('1', response);
    return response;
  }).then(function(response) {
    console.log('2', response);
    return true;
  }).then(function(response) {
    console.log('3', response); // response expected to be 'true'
    return async3();
  }).then(function(response) {
    console.log('4', response);
    return async4();
  });
}

function implementation2() {
  logContainer = document.body.appendChild(document.createElement("div"));
  console.log("Implementation 2");
  var serverSidePromiseChain;
  serverSidePromiseChain = async().then(function(response) {
    console.log('1', response);
    return response;
  });
  serverSidePromiseChain.then(function(response) {
    console.log('2', response);
    return true;
  });
  serverSidePromiseChain.then(function(response) {
    console.log('3', response); // response expected to be 'true'
    return async3();
  });
  serverSidePromiseChain.then(function(response) {
    console.log('4', response);
    return async4();
  });
}

function implementation3() {
  logContainer = document.body.appendChild(document.createElement("div"));
  console.log("Implementation 3");
  var serverSidePromiseChain;
  serverSidePromiseChain = async().then(function(response) {
    console.log('1', response);
    return response;
  });
  serverSidePromiseChain = serverSidePromiseChain.then(function(response) {
    console.log('2', response);
    return true;
  });
  serverSidePromiseChain = serverSidePromiseChain.then(function(response) {
    console.log('3', response); // response expected to be 'true'
    return async3();
  });
  serverSidePromiseChain = serverSidePromiseChain.then(function(response) {
    console.log('4', response);
    return async4();
  });
}

var logContainer;
var console = {
  log: function() {
    logContainer.appendChild(document.createElement("div")).textContent = [].join.call(arguments, ", ");
  }
};

onload = function(){
  implementation1();
  setTimeout(implementation2, 10);
  setTimeout(implementation3, 20);
}
body > div {
  float: left;
  font-family: sans-serif;
  border: 1px solid #ddd;
  margin: 4px;
  padding: 4px;
  border-radius: 2px;
}

实现#1 和#3 是等效的。实施 #2 不同,因为那里没有链,所有回调都将在同一个承诺上执行。

现在让我们稍微讨论一下 promise 链。 specs 表示:

2.2.7 then must return a promise
2.2.7.1 If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x)
2.3.3 If x is a promise, adopt its state

基本上是根据 return 的承诺调用 then 另一个 promise,它根据 callback return value 得到 resolved/rejected。在您的情况下,您正在 returning 标量值,然后将其沿着链传播到下一个承诺。

在您的特定情况下,会发生以下情况:

  • #1:你有 7 个承诺(async 调用加上 4 个 then,加上来自 async3()/async4 的两个),serverSidePromiseChain将指向 then 编辑的最后一个承诺 return。现在,如果 async() 所承诺的 return 永远不会 resolved/rejected,那么 serverSidePromiseChain 也会处于相同的情况。与 async3()/async4() 相同,如果该承诺也不是 resolved/rejected
  • #2:then 在同一个承诺上被多次调用,创建了额外的承诺,但它们不会影响应用程序的流程。一旦 return 由 async() 编辑的承诺将被解决,所有回调将被执行,回调 return 将被丢弃
  • #3:这等同于#1,只是现在你显式地传递了创建的承诺。当承诺 returned async() 得到解决时,第一个回调将被执行,它用 true 解决下一个承诺,第二个回调将相同,第三个将具有如果 async3() 的承诺被拒绝,则有机会将链转换为失败的链,与 returns async4() 的承诺的回调相同。

Promise 链最适合实际的异步操作,操作依赖于前一个操作的结果,你不想写很多胶水代码,你也不想达到callback hell.

我在我的博客上写了一系列关于 promises 的文章,其中一篇描述了 promises 的链接特性可以找到 here;这篇文章是针对ObjectiveC的,但是原理是一样的。