不是承诺只是callback?

我一直在开发JavaScript几年,我根本不明白承诺的大惊小怪。

看来我所做的只是改变:

api(function(result){ api2(function(result2){ api3(function(result3){ // do work }); }); }); 

我可以使用类似asynchronous的库,像这样:

 api().then(function(result){ api2().then(function(result2){ api3().then(function(result3){ // do work }); }); }); 

哪些是更多的代码和更less的可读性。 我在这里没有获得任何东西,也不是突然神奇的“平坦”。 更不用说把事情转化为承诺。

那么,在这里承诺有什么大惊小怪呢?

承诺不是callback。 承诺代表了asynchronous操作未来结果 。 当然,按照你的方式写下来,你没有什么好处。 但是,如果按照它们被使用的方式编写它们,则可以使用类似于同步代码的方式编写asynchronous代码,并且更容易遵循:

 api().then(function(result){ return api2(); }).then(function(result2){ return api3(); }).then(function(result3){ // do work }); 

当然,代码不多,但更具可读性。

但是,这不是结束。 让我们发现真正的好处:如果您想检查任何步骤中的任何错误,该怎么办? 用callback做到这一点是有害的,但是有了承诺,这是一块蛋糕:

 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个调用apiapi2api3可以同时运行(例如,如果他们是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是asynchronouscallback。 他们不能做任何callback不能做的事情,而且你也会遇到与普通callback不同的问题。

但是,Promise不仅仅是callback。 他们是一个非常强大的抽象,允许更干净,更好,function性的代码与较less容易出错的样板。

那么主要的想法是什么?

Promise是代表单个(asynchronous)计算结果的对象。 他们只解决了这个结果一次。 这意味着一些事情:

承诺实施观察员模式:

  • 您不需要知道在任务完成之前将使用该值的callback。
  • 而不是期望callback作为你的函数的参数,你可以很容易地return一个Promise对象
  • 承诺将存储的价值,你可以透明地添加一个callback,只要你想要的。 当结果可用时它将被调用。 “透明”意味着当你有承诺并且给它添加一个callback时,结果是否已经到达,这对你的代码没有什么影响 – API和契约是一样的,简化了caching/记忆。
  • 您可以轻松地添加多个callback

承诺是可链接的 ( monadic ,如果你想的话):

  • 如果您需要转换承诺所代表的价值,则可以转化函数映射到承诺上,并获得表示转换结果的新承诺。 你不能同时获得价值来使用它,但你可以很容易地提升承诺上下文中的转换。 没有样板callback。
  • 如果要链接两个asynchronous任务,则可以使用.then()方法。 这将需要一个callback被调用的第一个结果,并返回承诺的结果callback的回报。

听起来很复杂? 代码示例的时间。

 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 */); 

如果看到这些方法的代码有助于理解, 这里有几行中最基本的承诺lib 。

什么是承诺的大惊小怪?

Promise抽象允许function更好的组合性。 例如,在链接旁边, all函数为多个并行等待promise的组合结果创build一个promise。

最后但并非最不重要的承诺来集成error handling。 计算结果可能是承诺是用价值来实现的,或者是有理由拒绝的。 所有的组合函数都会自动处理这个问题,并在promise链中传播错误,所以你不需要明确地关心它,而只需要简单的callback实现。 最后,您可以为所有发生的exception添加专用的错误callback。

更不用说把事情转化为承诺。

实际上,如果使用良好的承诺库,这实际上是微不足道的,请参阅如何将现有的callbackAPI转换为承诺?

除了奥斯卡和伯吉的真棒答案外 ,ES6的箭头functionPromise也从一颗温和的小蓝星变成了一个红巨星。 即将崩溃成超新星:

 api().then(result => api2()).then(result2 => api3()).then(result3 => console.log(result3)) 

除了其他答案外,ES2015语法还可以与承诺无缝融合,减less了更多的样板代码:

 // Sequentially: api1() .then(r1 => api2(r1)) .then(r2 => api3(r2)) .then(r3 => { // Done }); // Parallel: Promise.all([ api1(), api2(), api3() ]).then(([r1, r2, r3]) => { // Done }); 

承诺不是callback,都是编程习惯,促进asynchronous编程。 使用协程或生成器返回promise的asynchronous/等待风格的编程可以被认为是第三个这样的习惯用法。 不同编程语言(包括Javascript)的这些习语的比较在这里: https : //github.com/KjellSchubert/promise-future-task

没有承诺只是callback的包装

示例您可以使用javascript native承诺与节点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