我有一些我正在更新的旧Node.js代码。在这个过程中,我正在设计新模块以使用旧代码。我现在发现,与我第一次写这篇文章相反,我更依赖于使用ES6承诺而不是回调。所以现在我有一些函数的混合返回promises和一些回调 - 这是乏味的。我认为最终应该重构使用promises。但在此之前......
首选承诺的首选情况以及首选回调的情况是什么?
回调可以比承诺更好地处理任何类型的情况,反之亦然吗?
根据我到目前为止看到的情况,我真的看不出任何使用回调而不是承诺的理由。这是真的吗?
答案 0 :(得分:30)
首先,您几乎不想编写混合回调和承诺进行异步操作的代码。如果你正在转向承诺或引入一些承诺,那么你可能想要将同一部分代码中的回调重构为promises。对于适当类型的操作,承诺相对于普通回调有很多优点,在已经在代码区域工作时转换的努力是值得的。
承诺非常适合:
pending
,fulfilled
和rejected
以及状态从pending => fulfilled
或{{1}转换的位置然后可以不改变(单个单向转换)。普通回调适用于承诺无法做到的事情:
pending => rejected
的回调)而且,我还要添加Array.prototype.map()
。
EventEmitters非常适合:
关于将普通回调代码转换为Promises的说明
如果你的回调符合调用约定的节点,并且回调作为最后一个参数传递并且像EventEmitter
那样调用,那么你有点自动将父函数包装在node.js中带有callback(err, result)
的promise中或使用Bluebird promise library时使用Promise.promisify()
。
使用Bluebird,您甚至可以立即宣传整个模块(在node.js调用约定中使用异步回调),例如:
util.promisify()
在node.js版本8 +
中现在有const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.writeFileAsync("file.txt", data).then(() => {
// done here
}).catch(err => {
// error here
});
将使用node.js异步调用约定的异步函数转换为返回promise的函数。
来自the doc:
的示例util.promisify()
答案 1 :(得分:5)
它们都存在解决同样的问题,处理异步函数的结果。
回调往往更加冗长,如果您没有主动模块化您的功能,并发协调多个异步请求会导致callback hell。错误处理和跟踪往往不那么直接甚至令人困惑,因为可能有许多Error对象都会回到调用堆栈中的单个错误。错误,也需要传递回原始调用者,如果在回调链中使用匿名函数,则在确定抛出原始错误的位置时也会导致头部刮擦。回调的好处之一是它们只是简单的旧功能,除了知道异步操作如何工作之外,不需要任何额外的理解。
Promise更常见,因为它们需要更少的代码,更具可读性,因为它们像同步函数一样编写,具有单个错误通道,可以处理抛出的错误并在最新版本的Node中添加util.promisify()
。 js,可以将Error-First Callbacks转换为promises。现在还有async/await
making its way into Node.js,它们也与Promises接口。
这完全基于意见,所以它确实是关于你最熟悉的东西,但Promises和async/await
是回调的演变并增强了异步开发体验。这不是一种详尽的比较,而是对回调和承诺的高级评价。
答案 2 :(得分:1)
我不记得从哪里得到这些东西,但可能有助于更好地理解承诺。
承诺不是回调。 promise表示异步操作的未来结果。当然,按照你的方式写它们,你得到的好处不大。但是如果按照它们的使用方式编写它们,您可以以类似于同步代码的方式编写异步代码,并且更容易遵循: 的优点强> 1.回调的可读性 2.容易发现错误。 3.同时回调
<强> 1。回调的可读性 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。容易发现错误。 当然,代码不多,但更具可读性。但这不是结束。让我们发现真正的好处:如果您想检查任何步骤中的任何错误怎么办?用回调来做这件事真是太棒了,但有了承诺,这是小菜一碟:
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。同时回调甚至更好: 如果那些对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。