首页 文章

Node.js:何时使用Promises vs Callbacks

提问于
浏览
10

我有一些旧的Node.js代码,我正在更新 . 在这个过程中,我正在设计新模块以使用旧代码 . 我现在发现,与我第一次写这篇文章时相比,我更依赖于使用ES6承诺而不是回调 . 所以现在我有一些函数混合返回promises和一些回调 - 这是乏味的 . 我认为最终应该重构使用promises . 但在此之前......

什么样的承诺是首选的,哪些是首选的回调?

是否有任何类型的情况,回调可以比承诺更好地处理,反之亦然?

根据我到目前为止看到的情况,我真的看不出任何使用回调而不是承诺的理由 . 真的吗?

3 回答

  • 23

    首先,您几乎不想编写混合回调和承诺进行异步操作的代码 . 如果您转向承诺或引入一些承诺,那么您可能希望将相同代码段中的回调重构为promise . 对于适当类型的操作,承诺比普通回调有许多优点,在已经在代码区域中工作时转换的努力是值得的 .

    Promises are great for:

    • 监控同步操作

    • 只需要通知一次(通常是完成或错误)

    • 协调或管理多个异步操作,例如排序或分支异步操作或同时管理飞行中的多个操作

    • 从嵌套或深度嵌套的异步操作传播错误

    • 准备好使用async / await的代码(或现在使用它与转换器一起使用)

    • 适合Promise模型的操作只有三种状态: pendingfulfilledrejected 以及状态从 pending => fulfilledpending => rejected 过渡的情况不能改变(单个单向转换) .

    • 动态链接或链接异步操作(例如执行这两个异步操作,检查结果,然后根据中间结果确定要执行的其他异步操作)

    • 管理异步和同步操作的混合

    • 自动捕获并向上传播异步完成回调中发生的任何异常(在普通回调中,这些异常有时会以静默方式隐藏) .

    Plain callbacks are good for things that promises cannot do:

    • 同步通知(例如 Array.prototype.map() 的回调)

    • 可能多次出现的通知(因此需要多次调用回调) . Promise是一次性设备,不能用于重复通知 .

    • 无法映射到待处理,已履行,拒绝的单向状态模型的情况 .

    而且,我还要添加 EventEmitter .

    EventEmitters are great for:

    • 发布/订阅类型通知

    • 具有事件模型的接口,特别是当事件可以多次发生时(如流)

    • 当第三方代码想要参与或监视某些内容而没有任何API而不是eventEmitter时,松散耦合 . 无需设计API . 只需将eventEmitter设为public并定义一些事件以及随之而来的数据 .


    Notes about converting plain callback code to Promises

    如果你的回调符合调用约定的节点,并将回调作为最后一个参数传递并调用,如此 callback(err, result) ,那么你有点自动将父函数包装在node.js中的 util.promisify() 的promise中,或者如果使用Bluebird promise library,则使用Promise.promisify() .

    使用Bluebird,您甚至可以同时实现整个模块(在node.js调用约定中使用异步回调),例如:

    const Promise = require('bluebird');
    const fs = Promise.promisifyAll(require('fs'));
    
    fs.writeFileAsync("file.txt", data).then(() => {
        // done here
    }).catch(err => {
        // error here
    });
    

    In node.js version 8+

    现在 util.promisify() 会将使用node.js异步调用约定的异步函数转换为返回promise的函数 .

    the doc:中的示例

    const util = require('util');
    const fs = require('fs');
    
    const stat = util.promisify(fs.stat);
    
    // usage of promisified function
    stat('.').then((stats) => {
      // Do something with `stats`
    }).catch((error) => {
      // Handle the error.
    });
    
  • 1

    它们都存在解决同样的问题,处理异步函数的结果 .

    回调往往更加冗长,并且如果除了知道异步操作如何工作之外还需要任何额外的理解,那么同时协调多个异步请求会导致callback hell .

    Promise更常见,因为它们需要更少的代码,更具可读性,因为它们像同步函数一样编写,具有单个错误通道,可以处理抛出的错误并且在最新版本的Node.js中添加了util.promisify(),可以转换Error-First回应承诺 . 现在还有 async/await 也是making its way into Node.js,它们也与Promises接口 .

    这完全是基于意见的,所以它真的是关于你最熟悉的东西,但Promises和 async/await 是回调的演变并增强了异步开发体验 . 无论如何,这并不是一次详尽的比较,而是对回调和承诺的高级评价 .

  • 4

    我不记得从哪里得到这些东西,但可能有助于更好地理解承诺 .

    承诺不是回调 . promise表示异步操作的未来结果 . 当然,按照你的方式写它们,你得到的好处不大 . 但是如果按照它们的使用方式编写它们,您可以以类似于同步代码的方式编写异步代码,并且更容易遵循: ADVANTAGES 1.回调的可读性2.容易捕获错误 . 3.同时回调

    1. Readability over callbacks Promise提供了一种更简洁明了的方式来表示javascript中的顺序异步操作 . 它们实际上是用于实现与回调相同效果的不同语法 . 优点是提高了可读性 . 像这样的东西

    aAsync()   
    .then(bAsync)  
     .then(cAsync)   
    .done(finish);
    

    比将每个单独的函数作为回调传递一样,可读性更高

    Async(function(){     return bAsync(function(){         return cAsync(function(){             finish()         })     }) }); 
    //-------------------------------------------- api().then(function(result){  
       return api2(); }).then(function(result2){     return api3(); }).then(function(result3){      // do work });
    

    2. Easy to catch errors. 当然,代码不多,但更具可读性 . 但这不是结束 . 让我们发现真正的好处:如果您想检查任何步骤中的任何错误怎么办?用回调来做这件事就好了,但是有了承诺,这是小菜一碟:

    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 }); 
    /* Pretty much the same as a try { ... } catch block. 
    Even better: */
     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. Simultaneous callbacks And even better: 如果那些对api,api2,api3的3个调用可以同时运行(例如,如果它们是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 .

相关问题