一尘不染

Node.js:何时使用Promises vs Callbacks

node.js

我有一些要更新的旧Node.js代码。在此过程中,我正在设计新模块以与旧代码一起使用。我发现现在,与我最初写这篇文章时相反,我更多地依赖使用ES6
Promise,而不是回调。所以现在我混合了一些函数,这些函数返回Promise和一些采用回调-这很乏味。我认为最终应该重构使用诺言。但是在那之前…

在什么情况下首选promise和回调在哪里?

在某种情况下,回调能比承诺更好地处理吗,反之亦然?

根据到目前为止所见,我真的看不出有任何理由使用回调代替promise。真的吗?


阅读 245

收藏
2020-07-07

共1个答案

一尘不染

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

承诺非常适合:

  • 监视同步操作
  • 只需通知一次(通常是完成或错误)
  • 协调或管理多个异步操作,例如排序或分支异步操作或同时管理飞行中的多个操作
  • 从嵌套或深度嵌套的异步操作传播错误
  • 准备好使用异步/等待的代码(或现在将其与编译器一起使用)
  • 适合Promise模型的操作,其中只有三个状态:pendingfulfilled并且rejected状态从pending => fulfilled或从的状态转换pending => rejected不能更改(单个单向转换)。
  • 动态链接或链接异步操作(例如执行这两个异步操作,检查结果,然后根据中间结果决定要执行哪些其他异步操作)
  • 管理异步和同步操作的混合
  • 自动捕获并向上传播异步完成回调中发生的任何异常(在普通回调中,这些异常有时会被静默隐藏)。

普通的回调对于promise不能做的事情有好处:

  • 同步通知(例如的回调Array.prototype.map()
  • 通知可能会发生多次(因此需要多次调用回调)。承诺是一次性的设备,不能用于重复通知。
  • 无法映射到挂起,已实现,已拒绝的单向状态模型中的情况。

而且,我也将添加EventEmitter

EventEmitters适用于:

  • 发布/订阅类型通知
  • 与事件模型的接口,特别是事件可以多次发生(如流)时
  • 当第三方代码想要参与或监视某事时,而没有eventEmitter的API,则松散的耦合。无需设计API。只需公开一个eventEmitter并定义一些事件以及与之相关的数据即可。

有关将纯回调代码转换为Promises的说明

如果您的回调调用适合与作为最后一个参数传递,并呼吁这样的回调约定的节点callback(err, result),那么你有点自动换行父功能的承诺与util.promisify()在node.js中,或者使用蓝鸟承诺库,用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
});

在node.js版本8+中

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

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.
});
2020-07-07