鍍金池/ 教程/ HTML/ 番外篇之——使用 Async
第9章 增加標簽和標簽頁面
番外篇之——使用 Mongoose
番外篇之——使用 Async
第4章 實現(xiàn)用戶頁面和文章頁面
第12章 增加友情鏈接
第14章 增加頭像
第7章 實現(xiàn)分頁功能
第5章 增加編輯與刪除功能
第11章 增加文章檢索功能
第3章 增加文件上傳功能
番外篇之——部署到 Heroku
第2章 使用 Markdown
第13章 增加404頁面
第16章 增加日志功能
第1章 一個簡單的博客
番外篇之——使用 Handlebars
第10章 增加pv統(tǒng)計和留言統(tǒng)計
番外篇之——使用 Passport
第15章 增加轉(zhuǎn)載功能和轉(zhuǎn)載統(tǒng)計
第8章 增加存檔頁面
番外篇之——使用 generic pool
番外篇之——使用 _id 查詢
番外篇之——使用 Disqus
番外篇之——使用 KindEditor
第6章 實現(xiàn)留言功能

番外篇之——使用 Async

Async 是一個流行的異步編程類庫,提供了直接而強大的 JavaScript 異步功能。雖然是為 Node.js 設計的,但是它也可以直接在瀏覽器中使用。

Async 提供了大約 20 個函數(shù),包括常用的 map, reduce, filter, forEach 等等,也有常用的異步流程控制函數(shù),包括 parallel, series, waterfall 等等。所有這些函數(shù)都是假設你遵循了 Node.js 的約定:在異步函數(shù)的最后提供一個回調(diào)函數(shù)作為參數(shù)。

Async 包括三部分:

  1. 流程控制:簡化十種常見流程的處理
  2. 集合處理:如何使用異步操作處理集合中的數(shù)據(jù)
  3. 工具類:幾個常用的工具類 這里我們不會講解 Async 的使用,讀者可去以下鏈接學習 Async 的相關知識:

Async : https://github.com/caolan/async stackoverflow : http://stackoverflow.com/ Async詳解之一:流程控制 : http://freewind.me/blog/20120515/917.html Async詳解之二:工具類 : http://freewind.me/blog/20120517/931.html Async詳解之三:集合操作 : http://freewind.me/blog/20120518/932.html Nodejs異步流程控制Async : http://blog.fens.me/nodejs-async/

我們在操作數(shù)據(jù)庫的時候經(jīng)常會這樣寫,以 Post.getOne 為例:

Post.getOne = function(name, day, title, callback) { 
  mongodb.open(function (err, db) {
    if (err) { ... }
    db.collection('posts', function (err, collection) {
      if (err) { ... }
      collection.findOne({ ... }, function (err, doc) {
        if (err) { ... }
        collection.update({ ... }, function (err) {
          mongodb.close();
          callback( ... );
        });
      });
    });
  });
};

這就是典型的深度嵌套回調(diào),代碼看起來并不美觀。下面我們使用 Async 解決這個問題。

首先,在 package.json 中添加對 Async 的依賴:

"async": "*"

并 npm install 安裝 Async 包。

在使用 Async 之前,我們先學習下 async.waterfall 的基本用法。

waterfall(tasks, [callback]) :多個函數(shù)依次執(zhí)行,且前一個的輸出為后一個的輸入,即每一個函數(shù)產(chǎn)生的值,都將傳給下一個函數(shù)。如果中途出錯,后面的函數(shù)將不會被執(zhí)行。錯誤信息以及之前產(chǎn)生的結(jié)果,將傳給 waterfall 最終的 callback,一個簡單的例子:

var async = require('async');

async.waterfall([
    function(callback){
        callback(null, 'one', 'two');
    },
    function(arg1, arg2, callback){
        console.log('arg1 => ' + arg1);
        console.log('arg2 => ' + arg2);
        callback(null, 'three');
    },
    function(arg3, callback){
        console.log('arg3 => ' + arg3);
        callback(null, 'done');
    }
], function (err, result) {
   console.log('err => ' + err);
   console.log('result => ' + result);
});

運行結(jié)果為:

arg1 => one
arg2 => two
arg3 => three
err => null
result => done

將 callback(null, 'three'); 修改為:

callback('error occurred !', 'three');

運行結(jié)果為:

arg1 => one
arg2 => two
err => error occurred !
result => three

我們以修改 user.js 為例,將 user.js 修改如下:

var mongodb = require('./db');
var crypto = require('crypto');
var async = require('async');

function User(user) {
  this.name = user.name;
  this.password = user.password;
  this.email = user.email;
};

module.exports = User;

User.prototype.save = function(callback) {
  var md5 = crypto.createHash('md5'),
      email_MD5 = md5.update(this.email.toLowerCase()).digest('hex'),
      head = "http://www.gravatar.com/avatar/" + email_MD5 + "?s=48";
  var user = {
      name: this.name,
      password: this.password,
      email: this.email,
      head: head
  };
  async.waterfall([
    function (cb) {
      mongodb.open(function (err, db) {
        cb(err, db);
      });
    },
    function (db, cb) {
      db.collection('users', function (err, collection) {
        cb(err, collection);
      });
    },
    function (collection, cb) {
      collection.insert(user, {
        safe: true
      }, function (err, user) {
        cb(err, user);
      });
    }
  ], function (err, user) {
    mongodb.close();
    callback(err, user[0]);
  });
};

User.get = function(name, callback) {
  async.waterfall([
    function (cb) {
      mongodb.open(function (err, db) {
        cb(err, db);
      });
    },
    function (db, cb) {
      db.collection('users', function (err, collection) {
        cb(err, collection);
      });
    },
    function (collection, cb) {
      collection.findOne({
        name: name
      }, function (err, user) {
        cb(err, user);
      });
    }
  ], function (err, user) {
    mongodb.close();
    callback(err, user);
  });
};

關于 Async 的使用詳見 https://github.com/caolan/async ,讀者可自行完成剩余的修改工作。