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阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容