Nodejs回调方法或承诺?

时间:2017-03-11 13:45:29

标签: javascript node.js asynchronous

我是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还是导出函数是一个很好的解决方案?

2 个答案:

答案 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,或下载bluebirdq

等模块,则可以使用

Promise 祝你好运。