我正在阅读this article,关于承诺抽象的部分对我来说似乎有点过于复杂。以下是一个例子:
requestSomeData("http://example.com/foo") // returns a promise for the response
.then(function(response){ // ‘then’ is used to provide a promise handler
return JSON.parse(response.body); // parse the body
}) // returns a promise for the parsed body
.then(function(data){
return data.price; // get the price
}) // returns a promise for the price
.then(function(price){ // print out the price when it is fulfilled
print("The price is " + price);
});
在我看来,以下代码可以用更少的代码行提供相同的结果:
requestSomeData("http://example.com/foo")
.requestHandler(function(response){
// parse the body
var data = JSON.parse(response.body);
// get the price
var price = data.price;
// print out the price
print("The price is " + price);
});
答案 0 :(得分:17)
虽然两者最终都会完成同样的事情,但不同之处在于你的第二个例子不是异步的。例如,考虑如果JSON.parse(...)
证明是一项非常昂贵的操作会发生什么;你必须挂起,直到一切都结束,这可能并不总是你想要的。
这就是承诺给你的东西:强大的能力将正确答案的计算推迟到更方便的时间。顾名思义,构造“承诺”在某个时刻给你结果,但不一定就是现在。您可以阅读更多关于期货和承诺的更多信息 here 。
答案 1 :(得分:3)
让我们将promise示例与纯Javascript示例进行比较:
// First we need a convenience function for W3C's fiddly XMLHttpRequest.
// It works a little differently from the promise framework. Instead of
// returning a promise to which we can attach a handler later with .then(),
// the function accepts the handler function as an argument named 'callback'.
function requestSomeDataAndCall(url, callback) {
var req = new XMLHttpRequest();
req.onreadystatechange = resHandler;
req.open("GET", url, false);
req.send();
function resHandler() {
if (this.readyState==4 && this.status==200) {
callback(this);
} else {
// todo: Handle error.
}
}
}
requestSomeDataAndCall("http://example.com/foo", function(res){
setTimeout(function(){
var data = JSON.parse(res.responseText);
setTimeout(function(){
var price = data.price;
setTimeout(function(){
print("The price is "+price);
},10);
},10);
},10);
});
正如Norbert Hartl所指出的那样,JSON.parse()将挂起浏览器以获取大字符串。所以我使用setTimeout()来延迟执行(暂停10毫秒后)。这是Kris Kowal解决方案的一个例子。它允许当前的Javascript线程完成,释放浏览器以呈现DOM更改并在回调运行之前滚动用户的页面。
我希望commonjs promise框架也使用类似setTimeout的东西,否则文章示例中的后续承诺确实会像恐惧一样同步运行。
我上面的替代方案看起来很丑陋,后面的流程需要进一步缩进。我重新组织了代码,以便我们可以在一个级别提供我们的流程链:
function makeResolver(chain) {
function climbChain(input) {
var fn = chain.shift(); // This particular implementation
setTimeout(function(){ // alters the chain array.
var output = fn(input);
if (chain.length>0) {
climbChain(output);
}
},10);
}
return climbChain;
}
var processChain = [
function(response){
return JSON.parse(response.body);
},
function(data){
return data.price; // get the price
},
function(price){
print("The price is " + price);
}
];
var climber = makeResolver(promiseChain);
requestSomeDataAndCall("http://example.com/foo", climber);
我希望证明Javascript中的传统回调传递几乎等同于承诺。然而,经过两次尝试,我似乎已经展示了,参考原始示例中代码的整洁性,这些承诺是一个更优雅的解决方案!
答案 2 :(得分:1)
第二个代码段易受拒绝服务攻击,因为example.com/foo只能返回无效的json以使服务器崩溃。即使空响应也是无效的JSON(尽管有效的JS)。这就像mysql_*
示例,带有明显的SQL注入漏洞。
承诺代码也可以改进。这些是平等的:
requestSomeData("http://example.com/foo") // returns a promise for the response
.then(function(response){ // ‘then’ is used to provide a promise handler
// parse the body
var data = JSON.parse(response.body);
// get the price
var price = data.price;
// print out the price
print("The price is " + price);
});
和
requestSomeData("http://example.com/foo")
.requestHandler(function(response){
try {
var data = JSON.parse(response.body);
}
catch(e) {
return;
}
// get the price
var price = data.price;
// print out the price
print("The price is " + price);
});
如果我们想要处理错误,那么这些将是相同的:
requestSomeData("http://example.com/foo") // returns a promise for the response
.then(function(response){ // ‘then’ is used to provide a promise handler
// parse the body
var data = JSON.parse(response.body);
// get the price
var price = data.price;
// print out the price
print("The price is " + price);
}).catch(SyntaxError, function(e) {
console.error(e);
});
和
requestSomeData("http://example.com/foo")
.requestHandler(function(response){
try {
var data = JSON.parse(response.body);
}
catch(e) {
//If the above had a typo like `respons.body`
//then without this check the ReferenceError would be swallowed
//so this check is kept to have as close equality as possible with
//the promise code
if(e instanceof SyntaxError) {
console.error(e);
return;
}
else {
throw e;
}
}
// get the price
var price = data.price;
// print out the price
print("The price is " + price);
});
答案 3 :(得分:0)