Node.js:何时使用承诺与回调
我正在更新一些旧的Node.js代码。在这个过程中,我正在设计新的模块来处理旧代码。我发现现在,与我第一次写这篇文章时不同,我更依赖于使用ES6承诺,而不是回调。所以现在我有一些函数返回承诺,一些函数接受回调,这很无聊。我认为最终它应该被重构以使用承诺。但在这之前 在什么情况下,承诺是首选的,回访是首选的 有没有哪种情况是回调能比承诺更好地处理的,反之亦然Node.js:何时使用承诺与回调,node.js,callback,promise,es6-promise,Node.js,Callback,Promise,Es6 Promise,我正在更新一些旧的Node.js代码。在这个过程中,我正在设计新的模块来处理旧代码。我发现现在,与我第一次写这篇文章时不同,我更依赖于使用ES6承诺,而不是回调。所以现在我有一些函数返回承诺,一些函数接受回调,这很无聊。我认为最终它应该被重构以使用承诺。但在这之前 在什么情况下,承诺是首选的,回访是首选的 有没有哪种情况是回调能比承诺更好地处理的,反之亦然 根据我到目前为止所看到的,我真的看不出有任何理由使用回调而不是承诺。这是真的吗?它们的存在都是为了解决同一个问题,处理异步函数的结果 回调往
根据我到目前为止所看到的,我真的看不出有任何理由使用回调而不是承诺。这是真的吗?它们的存在都是为了解决同一个问题,处理异步函数的结果 回调往往更加冗长,如果您没有积极地模块化您的函数,那么并发地协调多个异步请求可能会导致错误。错误处理和跟踪往往不那么直截了当,甚至令人困惑,因为可能有许多错误对象,它们都会返回到调用堆栈更深处的单个错误。如果在回调链中使用匿名函数,则在确定原始错误被抛出的位置时,还需要将错误传递回原始调用方,这也可能会导致一些头痛。回调的好处之一是,它们只是简单的旧函数,除了知道异步操作如何工作之外,不需要任何额外的理解 承诺更常见,因为它们需要的代码更少,可读性更强,因为它们像同步函数一样编写,具有单个错误通道,可以处理抛出的错误,并且在最新版本的Node.js中添加后,可以将错误优先回调转换为承诺。现在也有
async/await
,它们还与承诺接口
这完全是基于意见的,因此它实际上是关于您最熟悉的内容,但是,承诺和
async/await
是回调的演变,增强了异步开发体验。无论如何,这并不是一个详尽的比较,而是对回调和承诺的一个高层次的考察。首先,您几乎不想编写混合了回调和承诺的异步操作代码。如果您正转向承诺或引入一些承诺,那么您可能希望将同一段代码中的回调重构为承诺。对于适当类型的操作,与普通回调相比,承诺有许多优点,因此在已经在代码领域工作的情况下进行转换是非常值得的
承诺对以下方面很有用:
- 监视同步操作
- 只需通知一次(通常为完成或错误)
- 协调或管理多个异步操作,如排序或分支异步操作,或同时管理多个运行中的操作
- 从嵌套或深度嵌套的异步操作传播错误
- 准备好使用async/await的代码(或者现在将其与transpiler一起使用)
- 符合承诺模型的操作,其中只有三种状态:
、pending
和completed
,并且状态从rejected
或从pending=>completed
转换,则不能更改(单个单向转换)pending=>rejected
- 动态链接或链接异步操作(例如执行这两个异步操作,检查结果,然后根据中间结果决定执行哪些其他异步操作)
- 管理异步和同步操作的混合
- 自动捕获并向上传播异步完成回调中发生的任何异常(在普通回调中,这些异常有时是隐藏的)
- 同步通知(例如
的回调)Array.prototype.map()
- 可能发生多次的通知(因此需要多次调用回调)。承诺是一次性设备,不能用于重复通知
- 无法映射到挂起、已完成、已拒绝的单向状态模型的情况
EventEmitter
事件发射器非常适合:
- 发布/订阅类型通知
- 具有事件模型的接口,特别是当事件可以发生多次时(如流)
- 当第三方代码想要参与或监视某个东西而不使用任何API(而不是eventEmitter)时,会出现松散耦合。没有API可供设计。只需将eventEmitter公开,并定义一些事件及其附带的数据
关于将普通回调代码转换为承诺的说明 如果回调符合节点调用约定,将回调作为最后一个参数传递,并像这样调用
callback(err,result)
,那么您可以在node.js中使用util.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异步调用约定的异步函数转换为返回承诺的函数
来自
我不记得是从哪里得到这些东西的,但这可能有助于更好地理解承诺 承诺不是收回
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.
});
aAsync()
.then(bAsync)
.then(cAsync)
.done(finish);
aAsync(function(){
return bAsync(function(){
return cAsync(function(){
finish()
})
})
});
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.
});
Promise.all([api(), api2(), api3()])
.then(function(result) {
//do work. result is an array containing the values of the three fulfilled promises.
})
.catch(function(error) {
//handle the error. At least one of the promises rejected.
});