Node.js:何时使用Promises vs Callbacks

Sea*_*nch 16 callback node.js promise es6-promise

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

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

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

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

jfr*_*d00 33

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

承诺非常适合:

  • 监控同步操作
  • 这只需要通知一次(通常是完成或错误)
  • 协调或管理多个异步操作,例如排序或分支异步操作或同时管理飞行中的多个操作
  • 从嵌套或深度嵌套的异步操作传播错误
  • 为代码使用async/await准备好代码(或者现在使用它与转换程序一起使用)
  • 适合无极模式操作,其中只有三个状态:pending,fulfilledrejected何从状态转移pending => fulfilledpending => rejected可再不能改变(单一单向转换).
  • 动态链接或链接异步操作(例如执行这两个异步操作,检查结果,然后根据中间结果决定要执行哪些其他异步操作)
  • 管理异步和同步操作的混合
  • 自动捕获并向上传播在异步完成回调中发生的任何异常(在普通回调中,这些异常有时会被默默隐藏).

普通回调适用于承诺无法做到的事情:

  • 同步通知(例如回调Array.prototype.map())
  • 可能多次出现的通知(因此需要多次调用回调).Promise是一次性设备,不能用于重复通知.
  • 无法映射到待处理,已履行,拒绝的单向状态模型的情况.

而且,我还要添加EventEmitter混合.

EventEmitters非常适合:

  • 发布/订阅类型通知
  • 具有事件模型的接口,特别是当事件可以多次发生时(如流)
  • 当第三方代码想要参与或监视某些内容而没有任何API而不是eventEmitter时,松散耦合.没有API可供设计.只需将eventEmitter公开并定义一些事件以及随之而来的数据.

关于将普通回调代码转换为Promises的注意事项

如果你的回调适合调用约定的节点,并将回调作为最后一个参数传递并像这样调用callback(err, result),那么你有点自动将父函数包装util.promisify()在node.js 中的promise中,或者如果使用Bluebird promise库,则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
});
Run Code Online (Sandbox Code Playgroud)

在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.
});
Run Code Online (Sandbox Code Playgroud)


pet*_*teb 7

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

回调往往比较冗长,如果不主动模块化函数,则同时协调多个异步请求可能会导致回调地狱。错误处理和跟踪往往不太直接,甚至会造成混乱,因为可能有许多Error对象都返回到调用堆栈中的单个错误。错误也需要传递回原始调用者,如果在回调链中使用了匿名函数,则在确定原始错误在何处抛出时,也可能导致某些问题。回调的好处之一是它们只是普通的旧函数,除了了解异步操作的工作原理之外,不需要任何其他理解。

由于它们需要更少的代码,因此更常见,因为它们像同步函数一样编写,具有单个错误通道,可以处理引发的错误,并且util.promisify()在最新版本的Node.js中添加,并且可以转换Error-First Callbacks ,因此更具可读性承诺。现在也async/await有一种方法进入Node.js,并且它们也与Promises交互。

这完全基于观点,因此实际上是您最满意的,但Promises和async/await是回调的发展,并增强了异步开发的经验。无论如何,这都不是详尽的比较,而是对回调和Promise进行了高层次的研究。