[英]Nodejs callbacks to methods or promises?
我是Node的新手,我看到一個回調地獄模式出現在我的應用程序中,這使得它很難閱讀。
經過一番研究,互聯網提供了2種主要解決方案:
-導出功能
把這個:
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);
並使用承諾
我更傾向於函數的輸出,但是Internet表示,諾言是處理異步調用的更好的選擇。
我不想涉足某些領域,以后不得不改變我的編碼習慣/樣式以符合標准約定,最好從正確的道路入手。
因此,我應該只使用Promise還是函數導出是一個很好的解決方案?
現在,您還有第三個選擇,可以將其排除在純觀點之外:Promises + async
/ await
:
ES2017(將於6月發布的規范)將具有async
/ await
,這將提供更簡單的語法以在簡單的用例中使用Promise,並且NodeJS已在當前版本的Node v7(在撰寫本文時為v7.7.2)中支持它們。
使用promise和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
回調函數。
請注意,您只能在async
函數中使用await
(因為它們在后台管理promise)。 還要注意,NodeJS很快就會對未處理的承諾拒絕感到驚訝,因此請確保我們在go()
上進行了catch
。
我相信以上內容大致可轉換為以下內容:
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
/ await
為簡單的用例帶來了很多async
。 (對於更復雜的用例,您仍然必須依靠顯式的Promise處理。)
但是,這是否使我們脫離了觀點領域呢? 當然,無論您是否使用諾言,使函數小巧且可組合都是一件好事。
關於上面的p
:有各種各樣的庫供使用Node的標准回調機制編寫的API簡化。 在上面,我使用的是理論上的方法。 這是上面p
的一個非常非常非常簡單的實現:
const p = (f, ...args) => {
return new Promise((resolve, reject) => {
args.push((err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
f(...args);
});
};
exports.promisifier = p;
...但是您可以找到采用更全面方法的庫。
承諾之所以出色,是因為它們為代碼提供了一個更加“同步”的結構,即返回數據而不是為繼續提供異步回調。
假設您要編寫一個函數,該函數可讀取文件並提取其數據。 使用回調,您將編寫:
function readMyFile(cb) {
fs.readFile('/tmp/test', function (err, txt) {
if (err || !txt) {
return cb(null);
}
return cb(txt);
});
}
很好,但是它可以促進您提到的所有內容(回調地獄,准備不足等)。
使用promise,您將編寫:
function readMyFile() {
return fs.readFileAsync('/tmp/test');
}
(大多數API都有一個返回promise的實現,如果沒有,您可以包裝回調API使其返回promise)
當您使用promise時,您的方法實際上會返回某些內容(這就是我說promise使代碼具有更多“同步”結構的原因),它們返回的不是實際結果,而是一個promise或包裝該結果的句柄將來使用。 與回調相比,這有很大的好處,因為當您做出承諾時,您可以像對待回調一樣等待結果:
promise = readMyFile();
promise.then(function (data) {
...
});
您可以做一些在回調世界中會導致回調地獄的事情,例如在promise返回后做一些事情:
promise.then(sendDataToServe)
.then(storeServerReplyInDatabase);
一次等待幾個承諾:
Promise.all([promiseA, promiseB])
.then(function (resA, resB) {
...
});
更優雅地處理錯誤(而不是在各處傳遞“ err”參數):
Promise.all([promiseA, promiseB])
.then(function (resA, resB) {
...
})
.catch(function (err) {
console.error(err);
});
您已經看到,在回調上使用Promise可以促進一種更為優雅的編寫異步代碼的方式。
如果您使用的是JavaScript ES6的較新版本,或者通過下載bluebird或q等模塊,則可以使用Promise
祝好運。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.