Nodejs異步回調(diào)的優(yōu)雅處理方法
前言
Nodejs最大的亮點(diǎn)就在于事件驅(qū)動(dòng), 非阻塞I/O 模型,這使得Nodejs具有很強(qiáng)的并發(fā)處理能力,非常適合編寫(xiě)網(wǎng)絡(luò)應(yīng)用。在Nodejs中大部分的I/O操作幾乎都是異步的,也就是我們處理I/O的操作結(jié)果基本上都需要在回調(diào)函數(shù)中處理,比如下面的這個(gè)讀取文件內(nèi)容的函數(shù):
fs.readFile('/etc/passwd', function (err, data) {
if (err) throw err;
console.log(data);
});
那,我們讀取兩個(gè)文件,將這兩個(gè)文件的內(nèi)容合并到一起處理怎么辦呢?大多數(shù)接觸js不久的人可能會(huì)這么干:
fs.readFile('/etc/passwd', function (err, data) {
if (err) throw err;
fs.readFile('/etc/passwd2', function (err, data2) {
if (err) throw err;
// 在這里處理data和data2的數(shù)據(jù)
});
});
那要是處理多個(gè)類似的場(chǎng)景,豈不是回調(diào)函數(shù)一層層的嵌套啊,這就是大家常說(shuō)的回調(diào)金字塔或回調(diào)地獄(http://callbackhell.com/)的問(wèn)題,也是讓js小白最為頭疼的問(wèn)題。
這種層層嵌套的代碼給開(kāi)發(fā)帶來(lái)了很多問(wèn)題,主要體現(xiàn)在:
1.代碼可能性變差
2.調(diào)試?yán)щy
3.出現(xiàn)異常后難以排查
本文主要是介紹如何優(yōu)雅的處理以上異步回調(diào)問(wèn)題。
初級(jí)方案:通過(guò)遞歸處理異步回調(diào)
我們可以使用遞歸作為代碼的執(zhí)行控制工具。把需要執(zhí)行的操作封裝到一個(gè)函數(shù)中,在回調(diào)函數(shù)中通過(guò)遞歸調(diào)用控制代碼的執(zhí)行流程,廢話不多說(shuō),上個(gè)代碼吧:
var fs = require('fs');
// 要處理的文件列表
var files = ['file1', 'file2', 'file3'];
function parseFile () {
if (files.length == 0) {
return;
}
var file = files.shift();
fs.readFile(file, function (err, data) {
// 這里處理文件數(shù)據(jù)
parseFile(); // 處理完畢后,通過(guò)遞歸調(diào)用處理下一個(gè)文件
});
}
// 開(kāi)始處理
parseFile();
以上代碼已依次處理數(shù)組中的文件為例,介紹了通過(guò)遞歸的方式控制代碼的執(zhí)行流程。
應(yīng)用到一些簡(jiǎn)單的場(chǎng)景中還是不錯(cuò)的,比如:我們將一個(gè)數(shù)組中的數(shù)據(jù),依次保存到數(shù)據(jù)庫(kù)中就可以采用這種方式。
通過(guò)遞歸的方式可以解決一些簡(jiǎn)單的異步回調(diào)問(wèn)題。不過(guò)對(duì)于處理復(fù)雜的異步回調(diào)還是顯得有些無(wú)能為力(如需要同步多個(gè)異步操作的結(jié)果)。
華麗點(diǎn):采用Async、Q、Promise等第三方庫(kù)處理異步回調(diào)
為了更好的處理嵌套回調(diào)的問(wèn)題,可以考慮采用一些第三方專門(mén)處理異步的庫(kù),當(dāng)然有能力的完全可以自己寫(xiě)個(gè)異步處理的輔助工具。
比較常用的處理異步的庫(kù)有:async,q還有promise。從npmjs.org網(wǎng)站上來(lái)看,async的火熱程度最高。以前用過(guò)async,確實(shí)也挺方便的,各種異步處理的控制流實(shí)現(xiàn)的也挺好。
我們將最初的同時(shí)讀取兩個(gè)文件的代碼使用async處理下,示例如下:
var async = require('async')
, fs = require('fs');
async.parallel([
function(callback){
fs.readFile('/etc/passwd', function (err, data) {
if (err) callback(err);
callback(null, data);
});
},
function(callback){
fs.readFile('/etc/passwd2', function (err, data2) {
if (err) callback(err);
callback(null, data2);
});
}
],
function(err, results){
// 在這里處理data和data2的數(shù)據(jù),每個(gè)文件的內(nèi)容從results中獲取
});
通過(guò)async模塊,可以很好的控制異步的執(zhí)行流程了,也算是解決了層層回調(diào)的問(wèn)題,代碼比以前算是清晰了些,不過(guò)依舊還是離不開(kāi)回調(diào)函數(shù)。
想想如果能夠在不使用回調(diào)函數(shù)的情況下,處理異步,豈不是很爽,接下來(lái),我們談?wù)勈褂肊S6的新特性來(lái)實(shí)現(xiàn)這一目標(biāo)。
優(yōu)雅點(diǎn):擁抱ES6,替代回調(diào)函數(shù),解決回調(diào)地獄問(wèn)題
話說(shuō)EcmaScript Harmony (ES6)給js引入了不少新特性,對(duì)ES6不太了解的同學(xué),可以自行百度一下。
在nodejs中使用ES6的新特性,需要用v0.11.x以上的版本才行。
本文介紹的是使用Generator特性替代回調(diào)函數(shù),對(duì)Generator不了解?可以看看這里。
這里用到了co和thunkify兩個(gè)模塊,大家使用npm install命令安裝之。
還是以本文剛開(kāi)始提到的問(wèn)題為例,使用generator特性的實(shí)例代碼如下:
var fs = require('fs')
, co = require('co')
, thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);
co(function *() {
var test1 = yield readFile('test1.txt');
var test2 = yield readFile('test2.txt');
var test = test1.toString() + test2.toString();
console.log(test);
})();
處理代碼中的異常也是很簡(jiǎn)單的,只需要這樣就OK了:
try {
var test1 = yield readFile('test1.txt');
} catch (e) {
// 在這里處理異常
}
這種代碼是不是優(yōu)雅很多了?像寫(xiě)同步代碼一樣處理異步,是不是很爽!
nodejs領(lǐng)域中進(jìn)行Web開(kāi)發(fā),最火的框架莫過(guò)于express了,值得一提的是express的核心成員TJ大神有領(lǐng)導(dǎo)了一個(gè)新的Web框架——koa,宣稱是下一代的Web開(kāi)發(fā)框架,koa真是借助了ES6的generator這一特性,讓我們?cè)陂_(kāi)發(fā)Web系統(tǒng)的時(shí)候避免陷入層層的回調(diào)用。
總結(jié)
引用一下fibjs項(xiàng)目宣傳的一句話:Less Callback, More Girls - 更少回調(diào), 更多妹子
相關(guān)文章
在node.js中怎么屏蔽掉favicon.ico的請(qǐng)求
這篇文章主要介紹了在node.js中怎么屏蔽掉favicon.ico的請(qǐng)求,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03Nodejs express框架一個(gè)工程中同時(shí)使用ejs模版和jade模版
這篇文章主要介紹了Nodejs express框架一個(gè)工程中同時(shí)使用ejs模版和jade模版 的相關(guān)資料,需要的朋友可以參考下2015-12-12Node.js創(chuàng)建一個(gè)簡(jiǎn)單的服務(wù)器的實(shí)現(xiàn)
Node.js是一個(gè)基于Chrome V8引擎的JavaScript運(yùn)行時(shí)環(huán)境,可以在服務(wù)器端運(yùn)行JavaScript代碼,本文主要介紹了Node.js創(chuàng)建一個(gè)簡(jiǎn)單的服務(wù)器的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12利用Node.js編寫(xiě)跨平臺(tái)的spawn語(yǔ)句詳解
Node.js 最強(qiáng)大的一點(diǎn)就是“跨平臺(tái)”。只要在編碼時(shí)稍微注意一下,你的代碼就通吃 Windows、Linux 和 OSX 平臺(tái)。下面這篇文章主要介紹了如何利用Node.js編寫(xiě)跨平臺(tái)的spawn語(yǔ)句,需要的朋友可以參考借鑒。2017-02-02詳解Wondows下Node.js使用MongoDB的環(huán)境配置
這篇文章主要介紹了詳解Wondows下Node.js使用MongoDB的環(huán)境配置,這里使用到了Mongoose驅(qū)動(dòng)來(lái)讓JavaScript操作MongoDB,需要的朋友可以參考下2016-03-03使用Node.js寫(xiě)一個(gè)代碼生成器的方法步驟
這篇文章主要介紹了使用 Node.js 寫(xiě)一個(gè)代碼生成器,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-05-05詳解如何使用Node.js編寫(xiě)命令工具——以vue-cli為例
本篇文章主要介紹了如何使用Node.js編寫(xiě)命令工具——以vue-cli為例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06Node.js?使用?zlib?內(nèi)置模塊進(jìn)行?gzip?壓縮
這篇文章主要介紹了Node.js?使用?zlib?內(nèi)置模塊進(jìn)行?gzip?壓縮,nodejs為我們提供了一個(gè)zlib內(nèi)置模塊,我們可以使用它其中的gzip方法來(lái)對(duì)傳遞的數(shù)據(jù)進(jìn)行壓縮,從而提高數(shù)據(jù)傳遞效率,更多相關(guān)內(nèi)容需要的朋友可以參考一下2022-09-09