我遇到了一些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')会改变行为吗?承诺是否要求所有返回的值都是异步承诺以保持行为?
答案 0 :(得分:23)
您正在说明链接和分支之间的不同。链接wil序列多个异步操作,因此当前一个完成时开始,你可以链接任意数量的项目,依次排序。
当一个触发操作完成时,分支将多个异步操作挂钩到同一时间。
实现1和3是相同的。他们被束缚住了。实现3只使用临时变量进行链接,而实现1只是直接使用.then()
的返回值。执行没有区别。这些.then()
处理程序将以串行方式调用。
实施2是不同的。它是分支的,不是链式的。因为所有后续的.then()
处理程序都附加到完全相同的serverSidePromiseChain
承诺,所以它们都只等待第一个承诺被解析,然后所有后续的异步操作都在同一时间进行(不是连续的)和其他两个选项一样)。
了解这一点可能有助于将一个级别深入了解如何使用promises。
当你这样做时(方案1和3):
p.then(...).then(...)
发生的情况如下:
p
变量,在其上找到.then()
方法并调用它。.then()
方法只存储它传递的回调,然后返回一个新的promise对象。它此时不会调用它的回调。这个新的promise对象与原始的promise对象和它存储的回调相关联。在两者都满意之前,它不会解决。.then()
处理程序。同样,该承诺上的.then()
处理程序只存储.then()
回调,但它们尚未执行。p
将通过自己的异步操作得到解决。解决后,它会调用它存储的任何resolve
处理程序。其中一个处理程序将是对上述链中第一个.then()
处理程序的回调。如果该回调运行完成并返回任何内容或静态值(例如,不返回promise本身),那么它将解析它创建的在第一次调用.then()
后返回的承诺。当该承诺得到解决后,它将调用由上面的第二个.then()
处理程序安装的解析处理程序,依此类推。执行此操作时(方案2):
p.then();
p.then();
此承诺p
此处已存储.then()
次调用的解析处理程序。当原始承诺p
得到解决后,它将调用两个.then()
处理程序。如果.then()
处理程序本身包含异步代码并返回promises,则这两个异步操作将同时处于飞行状态(类似并行行为),而不是按照场景1和3中的顺序执行。
答案 1 :(得分:3)
实施#1和#3是等效的。实现#2不同,因为那里没有链,所有回调都将在同一个承诺上执行。
现在让我们讨论一下承诺链。 specs告诉我:
2.2.7
then
必须返回承诺 2.2.7.1如果onFulfilled
或onRejected
返回值x
,请运行承诺解决程序[[Resolve]](promise2, x)
2.3.3如果x
是承诺,则采用其状态
基本上在承诺上调用then
会返回另一个promise
,根据callback return value
进行解析/拒绝。在您的情况下,您将返回标量值,然后在链中传播到下一个承诺。
在您的特定情况下,会发生以下情况:
async
来电加上4个then
以及来自async3()
/ async4
的两个),serverSidePromiseChain
会指出到then
返回的最后一个承诺。现在,如果async()
返回的承诺永远不会被解决/拒绝,那么serverSidePromiseChain
也将处于相同的情况。与async3()
/ async4()
相同,如果该承诺也未得到解决/拒绝then
在同一个承诺上被多次调用,创建了额外的承诺,但它们不会影响应用程序的流程。一旦async()
返回的承诺得到解决,所有回调都将被执行,回调返回的内容将被丢弃async()
得到解决时,将执行第一个回调,它将使用true
解析下一个承诺,第二个回调将相同,第三个将有机会转换链失败的,如果async3()
的承诺被拒绝,与返回async4()
的承诺的回调相同。Promise链最适合实际的异步操作,其中操作依赖于前一个操作的结果,并且您不想编写大量的粘合代码,并且您也不希望到达{{ 3}}
我在博客上写了一系列关于承诺的文章,其中一篇描述了承诺的链接特征callback hell;该文章针对ObjectiveC,但原则是相同的。
答案 2 :(得分:1)
实施1和3似乎是等效的。
在实现2中,最后3个.then()
函数都对相同的承诺起作用。 .then()
方法返回一个新的promise。履行承诺的价值无法改变。见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;
}