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 方法的第二个参数处理错误。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容