我已经开发了几年的JavaScript,我根本不理解有关承诺的大惊小怪。
似乎我所做的只是改变:
api(function(result){
api2(function(result2){
api3(function(result3){
// do work
});
});
});
无论如何,我可以使用像async这样的库,例如:
api().then(function(result){
api2().then(function(result2){
api3().then(function(result3){
// do work
});
});
});
哪个代码更多,可读性更低。我没有在这里获得任何东西,它也不会突然神奇地“平坦”。更不用说必须将事物转换为承诺。
那么,这里的承诺有什么大惊小怪?
承诺不是回调。 promise表示异步操作的未来结果。当然,按照你的方式写它们,你得到的好处不大。但是如果按照它们的使用方式编写它们,您可以以类似于同步代码的方式编写异步代码,并且更容易遵循:
api().then(function(result){
return api2();
}).then(function(result2){
return api3();
}).then(function(result3){
// do work
});
当然,代码不多,但更具可读性。
但这不是结束。让我们发现真正的好处:如果您想检查任何步骤中的任何错误怎么办?用回调来做这件事就好了,但有了承诺,这是小菜一碟:
api().then(function(result){
return api2();
}).then(function(result2){
return api3();
}).then(function(result3){
// do work
}).catch(function(error) {
//handle any error that may occur before this point
});
与try { ... } catch
块几乎相同。
更好的是:
api().then(function(result){
return api2();
}).then(function(result2){
return api3();
}).then(function(result3){
// do work
}).catch(function(error) {
//handle any error that may occur before this point
}).then(function() {
//do something whether there was an error or not
//like hiding an spinner if you were performing an AJAX request.
});
甚至更好:如果那些3次调用api
,api2
,api3
可以同时运行(例如,如果它们是AJAX调用)但你需要等待这三个呢?没有承诺,你应该创建某种计数器。承诺,使用ES6符号,是另一块蛋糕,非常整洁:
Promise.all([api(), api2(), api3()]).then(function(result) {
//do work. result is an array contains the values of the three fulfilled promises.
}).catch(function(error) {
//handle the error. At least one of the promises rejected.
});
希望你现在以新的眼光看待Promise。
是的,Promises是异步回调。他们不能做回调不能做的任何事情,并且你遇到与异步回调相同的异步问题。
然而,Promise不仅仅是回调。它们是一个非常强大的抽象,允许更清晰,更好,功能更强的代码,更容易出错。
那么主要想法是什么?
Promise是表示单个(异步)计算结果的对象。他们resolve to that result只有一次。这意味着什么:
Promise实现了一个观察者模式:
return
一个Promise对象Promises are chainable(monadic,if you want):
.then()
方法。它将使用第一个结果调用回调,并返回回调返回的承诺结果的承诺。听起来很复杂?代码示例的时间。
var p1 = api1(); // returning a promise
var p3 = p1.then(function(api1Result) {
var p2 = api2(); // returning a promise
return p2; // The result of p2 …
}); // … becomes the result of p3
// So it does not make a difference whether you write
api1().then(function(api1Result) {
return api2().then(console.log)
})
// or the flattened version
api1().then(function(api1Result) {
return api2();
}).then(console.log)
展平不是神奇的,但你可以很容易地做到。对于重度嵌套的示例,(近似)等价物将是
api1().then(api2).then(api3).then(/* do-work-callback */);
如果看到这些方法的代码有助于理解,here's a most basic promise lib in a few lines。
什么是承诺的大惊小怪?
Promise抽象允许更好的函数可组合性。例如,在then
旁边进行链接,all
函数为多个并行等待的承诺的组合结果创建了一个承诺。
最后但并非最不重要的Promise带有集成的错误处理。计算的结果可能是承诺是用值来实现的,或者是因为理由而被拒绝。所有组合函数都自动处理这个并在promise链中传播错误,因此您无需在任何地方显式地关注它 - 与普通回调实现相反。最后,您可以为所有发生的异常添加专用错误回调。
更不用说必须将事物转换为承诺。
这实际上是很好的承诺库,请参阅How do I convert an existing callback API to promises?
除了已经建立的答案之外,凭借ES6箭头功能,Promise从一个中等闪亮的小蓝矮星直接变成一个红色巨人。那即将崩溃成超新星:
api().then(result => api2()).then(result2 => api3()).then(result3 => console.log(result3))
正如oligofren指出的那样,在api调用之间没有参数,你根本不需要匿名包装函数:
api().then(api2).then(api3).then(r3 => console.log(r3))
最后,如果你想达到一个超大质量的黑洞水平,可以期待Promises:
async function callApis() {
let api1Result = await api();
let api2Result = await api2(api1Result);
let api3Result = await api3(api2Result);
return api3Result;
}
除了其他答案之外,ES2015语法与promises无缝融合,减少了更多的样板代码:
// Sequentially:
api1()
.then(r1 => api2(r1))
.then(r2 => api3(r2))
.then(r3 => {
// Done
});
// Parallel:
Promise.all([
api1(),
api2(),
api3()
]).then(([r1, r2, r3]) => {
// Done
});
除了上面的精彩答案,还可以添加2个点:
1.语义差异:
承诺可能在创建时已经解决。这意味着他们保证条件而不是事件。如果它们已经被解析,则仍然会调用传递给它的已解析函数。
相反,回调处理事件。因此,如果您感兴趣的事件发生在回调注册之前,则不会调用回调。
2.控制倒置
回调涉及控制的倒置。当您使用任何API注册回调函数时,Javascript运行时会存储回调函数,并在准备好运行后从事件循环中调用它。
请参阅The Javascript Event loop以获得解释。
使用Promise,控制驻留在调用程序中。如果我们存储promise对象,则可以随时调用.then()方法。
Promise不是回调,两者都是促进异步编程的编程习惯。使用返回promises的协程或生成器的async / await-style编程可以被认为是第三种这样的习惯用法。这些成语跨不同编程语言(包括Javascript)的比较在这里:https://github.com/KjellSchubert/promise-future-task
没有承诺只是回调的包装
示例您可以将javascript本机promises与节点js一起使用
my cloud 9 code link : https://ide.c9.io/adx2803/native-promises-in-node
/**
* Created by dixit-lab on 20/6/16.
*/
var express = require('express');
var request = require('request'); //Simplified HTTP request client.
var app = express();
function promisify(url) {
return new Promise(function (resolve, reject) {
request.get(url, function (error, response, body) {
if (!error && response.statusCode == 200) {
resolve(body);
}
else {
reject(error);
}
})
});
}
//get all the albums of a user who have posted post 100
app.get('/listAlbums', function (req, res) {
//get the post with post id 100
promisify('http://jsonplaceholder.typicode.com/posts/100').then(function (result) {
var obj = JSON.parse(result);
return promisify('http://jsonplaceholder.typicode.com/users/' + obj.userId + '/albums')
})
.catch(function (e) {
console.log(e);
})
.then(function (result) {
res.end(result);
}
)
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
})
//run webservice on browser : http://localhost:8081/listAlbums
一点都不。
Callbacks只是函数在JavaScript中被调用,然后在另一个函数的执行完成后执行。怎么会发生?
实际上,在JavaScript中,函数本身被视为对象,因此作为所有其他对象,甚至函数也可以作为参数发送到其他functions。可以想到的最常见和通用的用例是JavaScript中的setTimeout()函数。
与使用回调相同的方法相比,Promises只是一种处理和构造异步代码的即兴方法。
Promise在构造函数中接收两个Callback:resolve和reject。 Promises中的这些回调为我们提供了对错误处理和成功案例的细粒度控制。当promise的执行成功执行并且拒绝回调用于处理错误情况时,将使用resolve回调。