如何優雅的處理Nodejs中的異步回調

前言

Nodejs最大的亮點就在於事件驅動, 非阻塞I/O 模型,這使得Nodejs具備很強的併發處理能力,很是適合編寫網絡應用。在Nodejs中大部分的I/O操做幾乎都是異步的,也就是咱們處理I/O的操做結果基本上都須要在回調函數中處理,好比下面的這個讀取文件內容的函數:html

fs.readFile('/etc/passwd', function (err, data) {
  if (err) throw err;
  console.log(data);
});

那,咱們讀取兩個文件,將這兩個文件的內容合併到一塊兒處理怎麼辦呢?大多數接觸js不久的人可能會這麼幹:node

fs.readFile('/etc/passwd', function (err, data) {
  if (err) throw err;
  fs.readFile('/etc/passwd2', function (err, data2) {
    if (err) throw err;
    // 在這裏處理data和data2的數據
  });
});

那要是處理多個相似的場景,豈不是回調函數一層層的嵌套啊,這就是你們常說的回調金字塔或回調地獄(http://callbackhell.com/)的問題,也是讓js小白最爲頭疼的問題。git

這種層層嵌套的代碼給開發帶來了不少問題,主要體如今:es6

  • 代碼可能性變差
  • 調試困難
  • 出現異常後難以排查

本文主要是介紹如何優雅的處理以上異步回調問題。github

初級方案:經過遞歸處理異步回調

咱們能夠使用遞歸做爲代碼的執行控制工具。把須要執行的操做封裝到一個函數中,在回調函數中經過遞歸調用控制代碼的執行流程,廢話很少說,上個代碼吧:數據庫

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) {
    // 這裏處理文件數據
    parseFile();  // 處理完畢後,經過遞歸調用處理下一個文件
  });
}

// 開始處理
parseFile();

以上代碼已依次處理數組中的文件爲例,介紹了經過遞歸的方式控制代碼的執行流程。express

應用到一些簡單的場景中仍是不錯的,好比:咱們將一個數組中的數據,依次保存到數據庫中就能夠採用這種方式。npm

經過遞歸的方式能夠解決一些簡單的異步回調問題。不過對於處理複雜的異步回調仍是顯得有些無能爲力(如須要同步多個異步操做的結果)。數組

華麗點:採用Async、Q、Promise等第三方庫處理異步回調

爲了更好的處理嵌套回調的問題,能夠考慮採用一些第三方專門處理異步的庫,固然有能力的徹底能夠本身寫個異步處理的輔助工具。promise

比較經常使用的處理異步的庫有:async,q還有promise。從npmjs.org網站上來看,async的火熱程度最高。之前用過async,確實也挺方便的,各類異步處理的控制流實現的也挺好。

咱們將最初的同時讀取兩個文件的代碼使用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的數據,每一個文件的內容從results中獲取
});

經過async模塊,能夠很好的控制異步的執行流程了,也算是解決了層層回調的問題,代碼比之前算是清晰了些,不過依舊仍是離不開回調函數。

想一想若是可以在不使用回調函數的狀況下,處理異步,豈不是很爽,接下來,咱們談談使用ES6的新特性來實現這一目標。

優雅點:擁抱ES6,替代回調函數,解決回調地獄問題

話說EcmaScript Harmony (ES6)給js引入了很多新特性,對ES6不太瞭解的同窗,能夠自行百度一下。

在nodejs中使用ES6的新特性,須要用v0.11.x以上的版本才行。

本文介紹的是使用Generator特性替代回調函數,對Generator不瞭解?能夠看看這裏

這裏用到了co和thunkify兩個模塊,你們使用npm install命令安裝之。

啓動時,爲了讓nodejs支持ES6的特性,須要附加--harmony參數,如:node --harmony index.js

仍是以本文剛開始提到的問題爲例,使用generator特性的實例代碼以下:

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);
})();

處理代碼中的異常也是很簡單的,只須要這樣就OK了:

try {
  var test1 = yield readFile('test1.txt');
} catch (e) {
  // 在這裏處理異常
}

這種代碼是否是優雅不少了?像寫同步代碼同樣處理異步,是否是很爽!

nodejs領域中進行Web開發,最火的框架莫過於express了,值得一提的是express的核心成員TJ大神又領導了一個新的Web框架——koa,宣稱是下一代的Web開發框架,koa真是藉助了ES6的generator這一特性,讓咱們在開發Web系統的時候避免陷入層層的回調用。

總結

引用一下fibjs項目宣傳的一句話:Less Callback, More Girls - 更少回調, 更多妹子

參考資料

http://blog.nodejs.org/2014/03/12/node-v0-11-12-unstable/

http://huangj.in/765

http://tobyho.com/2013/06/16/what-are-generators/

http://blog.shiqichan.com/using-es6-generators-in-nodejs/

http://www.html-js.com/article/1687

http://www.ituring.com.cn/article/62609

相關文章
相關標籤/搜索