nodejs解決回調地獄

1.async

https://github.com/caolan/async/blob/v1.5.2/README.md#times
https://github.com/bsspirit/async_demo 一些demo

var mysql = require('mysql');
var conn = mysql.createConnection({
    host: 'localhost',
    user: 'nodejs',
    password: 'nodejs',
    database: 'nodejs',
    port: 3306
});
conn.connect();

var insertSQL = 'insert into t_user(name) values("conan"),("fens.me")';
var selectSQL = 'select * from t_user limit 10';
var deleteSQL = 'delete from t_user';
var updateSQL = 'update t_user set name="conan update"  where name="conan"';

//delete
conn.query(deleteSQL, function (err0, res0) {
    if (err0) console.log(err0);
    console.log("DELETE Return ==> ");
    console.log(res0);

    //insert
    conn.query(insertSQL, function (err1, res1) {
        if (err1) console.log(err1);
        console.log("INSERT Return ==> ");
        console.log(res1);

        //query
        conn.query(selectSQL, function (err2, rows) {
            if (err2) console.log(err2);

            console.log("SELECT ==> ");
            for (var i in rows) {
                console.log(rows[i]);
            }

            //update
            conn.query(updateSQL, function (err3, res3) {
                if (err3) console.log(err3);
                console.log("UPDATE Return ==> ");
                console.log(res3);

                //query
                conn.query(selectSQL, function (err4, rows2) {
                    if (err4) console.log(err4);

                    console.log("SELECT ==> ");
                    for (var i in rows2) {
                        console.log(rows2[i]);
                    }
                });
            });
        });
    });
});

//conn.end();

//改進

var mysql = require('mysql');
var async = require('async');

var conn = mysql.createConnection({
    host: 'localhost',
    user: 'nodejs',
    password: 'nodejs',
    database: 'nodejs',
    port: 3306
});

var sqls = {
    'insertSQL': 'insert into t_user(name) values("conan"),("fens.me")',
    'selectSQL': 'select * from t_user limit 10',
    'deleteSQL': 'delete from t_user',
    'updateSQL': 'update t_user set name="conan update"  where name="conan"'
};

var tasks = ['deleteSQL', 'insertSQL', 'selectSQL', 'updateSQL', 'selectSQL'];
async.eachSeries(tasks, function (item, callback) {
    console.log(item + " ==> " + sqls[item]);
    conn.query(sqls[item], function (err, res) {
        console.log(res);
        callback(err, res);
    });
}, function (err) {
    console.log("err: " + err);
});

1. series(tasks, [callback]) (多個函數依次執行,之間沒有數據交換)

解決問題

step1(function(err, v1) {
     step2(function(err, v2) { 
        step3(function(err, v3) { // do somethig with the err or values v1/v2/v3 
        } 
     }
});
var async = require('async')
async.series([
  function(cb) { step1(function(err,v1) {
     // do something with v1
     cb(err, v1);
  }),
  function(cb) { step2(...) },
  function(cb) { step3(...) }
], function(err, values) {
// do somethig with the err or values v1/v2/v3
});

2. parallel(tasks, [callback]) (多個函數并行執行)

如果某個函數出錯,則立刻將err和已經執行完的函數的結果值傳給parallel最終的callback。其它未執行完的函數的值不會傳到最終數據,但要占個位置。
同時支持json形式的tasks,其最終callback的結果也為json形式。
示例代碼:

async.parallel([
    function(cb) { t.fire('a400', cb, 400) },
    function(cb) { t.fire('a200', cb, 200) },
    function(cb) { t.fire('a300', cb, 300) }
], function (err, results) {
    log('1.1 err: ', err); // -> undefined
    log('1.1 results: ', results); // ->[ 'a400', 'a200', 'a300' ]
});
async.parallel([
    function(cb) { log('1.2.1: ', 'start'); t.fire('a400', cb, 400) }, // 該函數的值不會傳給最終callback,但要占個位置
    function(cb) { log('1.2.2: ', 'start'); t.err('e200', cb, 200) },
    function(cb) { log('1.2.3: ', 'start'); t.fire('a100', cb, 100) }
], function(err, results) {
    log('1.2 err: ', err); // -> e200
    log('1.2 results: ', results); // -> [ , undefined, 'a100' ]
});

3. waterfall(tasks, [callback]) (多個函數依次執行,且前一個的輸出為后一個的輸入)

seires相似,按順序依次執行多個函數。不同之處,每一個函數產生的值,都將傳給下一個函數。如果中途出錯,后面的函數將不會被執行。錯誤信息以及之前產生的結果,將傳給waterfall最終的callback。

async.waterfall([
    function(cb) { log('1.1.1: ', 'start'); cb(null, 3); },
    function(n, cb) { log('1.1.2: ',n); t.inc(n, cb); },
    function(n, cb) { log('1.1.3: ',n); t.fire(n*n, cb); }
], function (err, result) {
    log('1.1 err: ', err); // -> null
    log('1.1 result: ', result); // -> 16
});

二、promise解決

fs.readFile("test.txt",function(err,data){
    if(!err){
        console.log(data);
    }else{
        console.error(err);
    }
});

我們可以把 fs.readFile函數封裝為 promise風格的函數,如下:

var preadFile = function(file){
  fs.readFile(file,function(err,data){
    var deferred = Q.defer();
    if(!err){
      deferred.resolve(data);
    }else{
      deferred.reject(err);
    }

    return deferred.promise;
  });
}
//then 的第一個參數是正確處理函數,第二個參數是錯誤處理函數
preadFile("test.txt").then(console.log,console.error);

1.各個回調函數順序傳遞數據:

var fun1 = function (data,cb) {
  cb(null,data+" fun1");
}

var fun2 = function (data,cb) {
  cb(null,data+" fun2");
}

var fun3 = function (data,cb) {
  cb(null,data+" fun3");
}

function main(data,cb){
  fun1(data,function(err,data){
    if(!err){
      fun2(data,function(err,data){
        if(!err){
          fun3(data,cb);
        }else{
          cb(err);
        }
      });
    }else{
      cb(err);
    }
  });
}
var fun1 = function (data,cb) {
  cb(null,data+" fun1");
}

var fun2 = function (data,cb) {
  cb(null,data+" fun2");
}

var fun3 = function (data,cb) {
  cb(null,data+" fun3");
}

function main(data,cb){
  fun1(data,function(err,data){
    if(!err){
      fun2(data,function(err,data){
        if(!err){
          fun3(data,cb);
        }else{
          cb(err);
        }
      });
    }else{
      cb(err);
    }
  });
}

解決方法

var Q = require("q");
var fun1 = function (data) {
  var deferred = Q.defer();
  deferred.resolve(data+" fun1");
  return deferred.promise;
}

var fun2 = function (data) {
  var deferred = Q.defer();
  deferred.resolve(data+" fun2");
  return deferred.promise;
}

var fun3 = function (data) {
  var deferred = Q.defer();
  deferred.resolve(data+" fun3");
  return deferred.promise;
}

function main(data,cb){
   fun2("test")
     .then(fun3)
     .then(fun4)
     .done(function(data){
       cb(null,data);//ok 獲得的最終數據為 --->"test fun1 fun2 fun3"
     },function(err){
       cb(err);//failed
     });
}

2.收集各個回調函數產生的數據:有時候我們需要執行很多回調函數,然手把這個回調函數的數據一齊傳遞給一個函數處理,此時我們可以使用 all 和 spread 方法,參看如下代碼:

var Q = require("q");
var fun1 = function (data) {
  var deferred = Q.defer();
  deferred.resolve(data+" fun1");
  return deferred.promise;
}

var fun2 = function (data) {
  var deferred = Q.defer();
  deferred.resolve(data+" fun2");
  return deferred.promise;
}

var fun3 = function (data) {
  var deferred = Q.defer();
  deferred.resolve(data+" fun3");
  return deferred.promise;
}

Q.all([
  fun2("test1"),fun3("test2"),fun4("test3")
  ]).spread(function(){
    console.log(arguments);//獲得的參數為('test1 fun1', 'test2 fun2', 'test3 fun3' )
  });

3.統一處理錯誤:傳統的回調函數方式使用的話,我們需要在每一個回調函數里判斷是否有錯誤需要處理,這樣會存在很多冗余代碼,使用 promise,可以使用 done 或者 fail在一個函數中處理錯誤,如第一個例子一樣,使用 done 方法的第二個參數處理錯誤。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容