Javascript 不是';不要承诺只是回电?

Javascript 不是';不要承诺只是回电?,javascript,callback,promise,q,bluebird,Javascript,Callback,Promise,Q,Bluebird,我开发JavaScript已经有几年了,我完全不理解关于承诺的大惊小怪 似乎我所做的就是改变: api(function(result){ api2(function(result2){ api3(function(result3){ // do work }); }); }); 不管怎样,我都可以使用这样的库,比如: api().then(function(result){ api2().then(funct

我开发JavaScript已经有几年了,我完全不理解关于承诺的大惊小怪

似乎我所做的就是改变:

api(function(result){
    api2(function(result2){
        api3(function(result3){
             // do work
        });
    });
});
不管怎样,我都可以使用这样的库,比如:

api().then(function(result){
     api2().then(function(result2){
          api3().then(function(result3){
               // do work
          });
     });
});
代码较多,可读性较差。我在这里什么也没得到,也不是突然间神奇的“平坦”。更不用说把事情变成承诺了


那么,承诺有什么大不了的呢?

承诺不是回扣。承诺表示异步操作的未来结果。当然,以你这样的方式写作,你几乎没有什么好处。但是,如果您按照预期的方式编写它们,那么您可以使用类似于同步代码的方式编写异步代码,并且更易于遵循:

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.
});
更妙的是:如果对
api
api2
api3
的这3个调用可以同时运行(例如,如果它们是AJAX调用),但您需要等待这三个调用呢?如果没有承诺,你就必须创建某种计数器。使用ES6符号表示的With Promissions是另一个小菜一碟,非常简洁:

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对象,而不是将回调作为函数的参数
  • 承诺将存储该值,您可以随时透明地添加回调。当结果可用时,将调用它。“透明性”意味着当你有一个承诺并向它添加一个回调时,结果是否已经到达对你的代码没有影响——API和契约是相同的,大大简化了缓存/备忘录
  • 您可以轻松添加多个回调
(一元的,):

  • 如果需要转换承诺所表示的值,可以将转换函数映射到承诺上,并返回表示转换结果的新承诺。您无法以某种方式同步获得使用它的值,但您可以轻松地在承诺上下文中提升转换。没有样板文件回调
  • 如果要链接两个异步任务,可以使用
    .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 */);
如果看到这些方法的代码有助于理解

承诺有什么大不了的

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

最后但并非最不重要的承诺是集成错误处理。计算的结果可能是要么用一个值来实现承诺,要么用一个原因来拒绝承诺。所有的组合函数都会自动处理这个问题,并在承诺链中传播错误,因此您不需要在任何地方都显式地关注它——这与普通回调实现不同。最后,您可以为所有发生的异常添加专用的错误回调

更不用说把事情变成承诺了


对于good promise库来说,这其实很简单,请参见

promise不是回调,它们都是便于异步编程的编程习惯用法。使用异步/等待风格的编程,使用返回承诺的协程或生成器,可以被认为是第三种这样的习惯用法。不同编程语言(包括Javascript)对这些习惯用法的比较如下:

没有承诺只是回调的包装

范例 您可以将javascript本机承诺与节点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

除了已经确定的答案外,使用ES6箭头功能,我们可以从一个稍微发光的小蓝矮星直接变成红巨星。即将坍缩成超新星:

api().then(result => api2()).then(result2 => api3()).then(result3 => console.log(result3))
如前所述,如果api调用之间没有参数,则根本不需要匿名包装函数:

api().then(api2).then(api3).then(r3 => console.log(r3))
最后,如果你想达到超大质量黑洞的水平,可以期待承诺:

async function callApis() {
    let api1Result = await api();
    let api2Result = await api2(api1Result);
    let api3Result = await api3(api2Result);

    return api3Result;
}

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

// 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。语义差异:

承诺可能已经在创造时得到解决。这意味着它们保证的是条件而不是事件。如果它们已经被解析,传递给它的已解析函数仍然被调用

相反,回调处理事件。因此,如果您感兴趣的事件发生在通话之前