我是Node的新手,我看到我的应用程序中出现了一个回调地狱模式,这使得它难以阅读。
经过一些研究,互联网提供了两个主要解决方案:
- 导出功能
转过来:
var fs = require('fs');
var myFile = '/tmp/test';
fs.readFile(myFile, 'utf8', function(err, txt) {
if (err) return console.log(err);
txt = txt + '\nAppended something!';
fs.writeFile(myFile, txt, function(err) {
if(err) return console.log(err);
console.log('Appended text!');
});
});
进入这个:
var fs = require('fs');
function notifyUser(err) {
if(err) return console.log(err);
console.log('Appended text!');
};
function appendText(err, txt) {
if (err) return console.log(err);
txt = txt + '\nAppended something!';
fs.writeFile(myFile, txt, notifyUser);
}
var myFile = '/tmp/test';
fs.readFile(myFile, 'utf8', appendText);
和 使用承诺
我更倾向于功能的输出,但互联网认为承诺是处理异步调用的更好选择。
我不想进入后来必须改变我的编码习惯/风格以符合标准惯例,最好从正确的道路开始。
我应该开始使用promises还是导出函数是一个很好的解决方案?
答案 0 :(得分:2)
你现在有第三种选择,可能会将其从纯粹意见领域中取出:承诺+ 4
/ 'retu'
:
ES2017(6月份发布的规范)将以async
/ await
为特色,它为简单用例中的承诺提供了更简单的语法,NodeJS已经在当前版本的Node v7中支持它们(撰写本文时的第7.7.2节)。
使用承诺和async
/ await
,您的代码将如下所示:
async
它仍然是异步的,只是简单承诺用例的语法更简单,允许您让代码反映逻辑而不需要中间await
回调函数。
请注意,您只能在const p = require(/*...some theoretical promisifier...*/).promisifier;
const fs = require('fs');
async function go() {
const myFile = '/home/tjc/temp/test';
let txt = await p(fs.readFile, myFile, 'utf8');
txt = txt + '\nAppended something!';
await p(fs.writeFile, myFile, txt);
console.log('Appended text!');
}
go().catch(error => {
console.log(error);
});
函数中使用then
(因为他们在幕后管理承诺)。另请注意,NodeJS很快就会对未处理的承诺拒绝感到高兴,因此请确保我们在await
上执行async
。
我相信以上内容大致转换为以下内容:
catch
...但当然如果你自己编写,你会以不同的方式组织它:
go()
正如您所看到的,就清晰度而言,const p = require(/*...some theoretical promisifier...*/).promisifier;
const fs = require('fs');
function go() {
const myFile = '/home/tjc/temp/test';
return p(fs.readFile, myFile, 'utf8').then(txt => {
txt = txt + '\nAppended something!';
return p(fs.writeFile, myFile, txt).then(() => {
console.log('Appended text!');
});
});
}
go().catch(error => {
console.log(error);
});
/ const p = require(/*...some theoretical promisifier...*/).promisifier;
const fs = require('fs');
function go() {
const myFile = '/home/tjc/temp/test';
return p(fs.readFile, myFile, 'utf8')
.then(txt => {
txt = txt + '\nAppended something!';
return p(fs.writeFile, myFile, txt);
})
.then(() => {
console.log('Appended text!');
});
}
go().catch(error => {
console.log(error);
});
为简单的用例带来了很多好处。 (对于更复杂的用例,您仍然需要回退到明确的承诺处理。)
但是,这是否使我们脱离了意见领域,这是另一个问题。当然,无论您是否使用承诺,保持您的功能小巧且可组合都是一件好事。
上面的关于async
:有各种各样的库用于宣传使用Node的标准回调机制编写的API;在上面我使用的是理论上的一个。以上是await
的一个非常非常简单的实现:
p
...但您可以找到采用更彻底方法的库。
答案 1 :(得分:0)
Promise很棒,因为它们为代码提供了更“同步”的结构,即返回数据而不是为继续提供异步回调。
让我们假设您要编写一个读入文件并提取其数据的函数。回调你会写:
function readMyFile(cb) {
fs.readFile('/tmp/test', function (err, txt) {
if (err || !txt) {
return cb(null);
}
return cb(txt);
});
}
这很好,但它会促进你提到的一切(回调地狱,缺乏准备等等)
使用promises,你会写:
function readMyFile() {
return fs.readFileAsync('/tmp/test');
}
(大多数API都有一个返回promise的实现,如果没有,你可以包装回调API以使它们返回一个promise)
当你使用promises时,你的方法实际上返回了一些东西(这就是为什么我说promises为代码提供了一个更“同步”的结构),他们返回的不是实际的结果,而是一个包装的承诺或句柄结果供将来使用。 这比回调有很大的好处,因为当你有一个承诺时,你可以像使用回调一样等待结果:
promise = readMyFile();
promise.then(function (data) {
...
});
你可以做一些在回调世界中会导致回调地狱的事情,比如在承诺返回后做一些事情:
promise.then(sendDataToServe)
.then(storeServerReplyInDatabase);
立刻等待几个承诺:
Promise.all([promiseA, promiseB])
.then(function (resA, resB) {
...
});
更优雅地处理错误(而不是在任何地方传递“错误”参数):
Promise.all([promiseA, promiseB])
.then(function (resA, resB) {
...
})
.catch(function (err) {
console.error(err);
});
你已经可以看到,使用promises而不是回调可以提供一种更优雅的方式来编写异步代码。
如果您使用较新版本的javascript ES6,或下载bluebird或q 等模块,则可以使用Promise 祝你好运。