了解JavaScript的承诺; 堆栈和链接

我已经遇到了一些JavaScript承诺的问题,尤其是堆叠链。

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

实施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(); }) 

链的一部分是否返回值(步骤2中为“true”)会改变行为吗? 承诺是否要求所有返回的值是asynchronous承诺以保持行为?

您正在说明链接和分支之间的不同之处。 链接序列多个asynchronous操作,所以一个开始时,前一个完成,你可以链任意数量的项目顺序一个接一个。

当一个触发操作完成时,分支挂接多个asynchronous操作,以便同时处于运行状态。

实现1和3是相同的。 他们被链接。 实现3只是使用一个临时variables来链接,而实现1只是直接使用.then()的返回值。 执行没有区别。 这些.then()处理程序将以串行方式调用。

实现2是不同的。 它是分支的,而不是链接的。 由于所有后续的.then()处理程序都附加到完全相同的serverSidePromiseChain承诺中,它们都只会等待第一个要解决的承诺,然后所有后续的asynchronous操作都将同时在运行(而不是像其他两个选项)。


了解这一点可能会有所帮助,将其中一个层次下降到承诺的工作方式。

当你做(场景1和3):

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

会发生什么如下:

  1. 解释器把你的pvariables,find.then()方法并调用它。
  2. .then()方法只存储它传递的callback,然后返回一个新的promise对象。 这个时候不会调用它的callback函数。 这个新的承诺对象与最初的承诺对象和它所存储的callback都有关系。 直到双方都满意后才能解决。
  3. 然后在那个新返回的promise上调用第二个.then()处理程序。 再一次,那个promise上的.then()处理程序只是存储.then()callback函数,而且还没有执行。
  4. 那么在将来的某个时候,原来的承诺p会通过自己的asynchronous操作来解决。 当它得到解决后,它会调用它存储的任何resolve处理程序。 其中一个处理程序将是上述链中第一个.then()处理程序的callback。 如果该callback运行到完成状态,并返回无或静态值(例如,本身不返回承诺),则它将parsing在首次调用.then()之后创build的承诺。 当这个承诺解决后,它会调用上面的第二个.then()处理程序安装的parsing处理程序等等。

当你这样做(场景2)时:

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

这里的一个承诺p已经存储了来自.then()调用的parsing处理程序。 当原始的promise被parsing的时候,它会调用两个.then()处理程序。 如果.then()处理程序本身包含asynchronous代码并返回promise,则这两个asynchronous操作将同时处于运行状态(类似并行的行为),而不是如场景1和3中的顺序。

实现#1和#3是等同的。 实现#2不同,因为那里没有链,所有的callback都将在相同的承诺上执行。

现在我们来讨论一下诺言链。 规格说明:

2.2.7 then必须返回一个承诺
onFulfilled如果onFulfilledonRejected返回值x ,则运行Promise Resolution Procedure [[Resolve]](promise2, x)
2.3.3如果x是一个承诺,则采用它的状态

then基本上调用一个承诺返回另一个promise ,根据callback return value到解决/拒绝。 在你的情况下,你正在返回标量值,然后传播到下一个承诺。

在你的具体情况下,会发生什么情况:

  • #1:你有7个承诺( async调用加4 then再加上两个来自async3() / async4 ), serverSidePromiseChain将指向最后返回的promise。 现在,如果由async()返回的promise始终不被parsing/拒绝,那么serverSidePromiseChain也将处于相同的情况。 与async3() / async4()如果承诺也没有解决/拒绝
  • #2: then在相同的承诺上被多次调用,额外的承诺被创build,但是它们不影响应用程序的stream程。 一旦由async()返回的promise将被parsing,所有的callback函数将被执行,callback函数返回的将被丢弃
  • #3:这相当于#1只有你明确地传递了创build的承诺。 当parsing了返回的promise后,第一个callback函数将被执行,第二个callback函数将返回true ,第三个callback函数将返回true ,第三个callback函数将会有机会转换为失败的if async3()的承诺被拒绝,与返回async4()的承诺的callback一样。

Promise链最适合实际的asynchronous操作,其操作取决于前一个操作的结果,而且您​​不想编写大量的胶水代码,也不希望触及到callback地狱 。

我在博客上写了一系列关于承诺的文章,其中一篇描述了承诺的链接特征。 这篇文章是针对ObjectiveC的,但是原理是一样的。

实施1和3似乎是等同的。

在实现2中,最后的3个.then()函数全都按照相同的承诺行事。 .then()方法返回一个新的承诺。 兑现承诺的价值不能改变。 见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; }