1

回调越来越成为编码中的一项要求,尤其是当您考虑Node.JS非阻塞工作方式时。但是写很多协程回调很快就会变得难以回读。

例如,想象像这样的末日金字塔

// This asynchronous coding style is really annoying. Anyone invented a better way yet?
// Count, remove, re-count (verify) and log.
col.count(quertFilter,          function(err, countFiltered) {
    col.count(queryCached,      function(err, countCached) {
        col.remove(query,       function(err) {
            col.count(queryAll, function(err, countTotal) {
                util.log(util.format('MongoDB cleanup: %d filtered and %d cached records removed. %d last-minute records left.', countFiltered, countCached, countTotal));
            });
        });
    });
});

是我们经常看到的东西,而且很容易变得更复杂。

当每个函数至少长几行时,分离函数开始变得可行:

// Imagine something more complex

function mary(data, pictures) {
    // Do something drastic
}

// I want to do mary(), but I need to write how before actually starting.

function nana(callback, cbFinal) {
    // Get stuff from database or something
    callback(nene, cbFinal, data);
}

function nene(callback, cbFinal, data) {
    // Do stuff with data
    callback(nini, cbFinal, data);
}

function nini(callback, data) {
    // Look up pictures of Jeff Atwood
    callback(data, pictures);
}

// I start here, so this story doesn't read like a book even if it's quite straightforward.

nana(nene, mary);

但是一直有很多路过的 vars 发生。在中间编写其他函数时,这变得难以阅读。这些功能本身可能太微不足道,无法证明给他们自己的文件是合理的。

4

3 回答 3

6

使用异步流控制库,例如async. 它提供了一种简洁的方式来构建需要多个异步调用的代码,同时保持它们之间存在的任何依赖关系(如果有的话)。

在您的示例中,您将执行以下操作:

async.series([
    function(callback) { col.count(queryFilter, callback); },
    function(callback) { col.count(queryCached, callback); },
    function(callback) { col.remove(query, callback); },
    function(callback) { col.count(queryAll, callback); }
], function (err, results) {
    if (!err) {
        util.log(util.format('MongoDB cleanup: %d filtered and %d cached records removed. %d last-minute records left.', 
            results[0], results[1], results[3]));
    }  
});

这将依次执行每个功能;一旦第一个调用它的回调,第二个被调用,依此类推。但是您也可以使用parallelorwaterfall或任何与您正在寻找的流程相匹配的流程。我发现它比使用 Promise 干净得多。

于 2013-01-10T13:54:40.383 回答
5

回调的另一种方法是承诺。

示例:jQuery Ajax。这个可能看起来很熟悉。

$.ajax({
  url: '/foo',
  success: function() {
      alert('bar');
  }  
});

但是 $.ajax 也返回一个承诺。

var request = $.ajax({
  url: '/foo'
});

request.done(function() {
    alert('bar');
});

一个好处是,您可以模拟同步行为,因为您可以使用返回的 Promise 而不是提供对 $.ajax.success 的回调以及对回调和回调的回调...。另一个优点是,您可以链接 /聚合承诺,如果你愿意,可以为一个承诺聚合提供错误处理程序。

我发现这篇文章非常有用。它描述了回调、承诺和其他技术的优缺点。

一个流行的实现(例如 AngularJS iirc 使用)是Q

于 2013-01-10T12:30:21.340 回答
2

综合答案和文章。请编辑此答案并以简单的方式添加库/示例/文档 URL,以使每个人都受益。

Promise 文档

异步库

  • 异步.js

    async.waterfall([
        function(){ // ... },
        function(){ // ... }
    ], callback);
    
  • 节点纤维

  • Step(
        function func1() {
            // ...
            return value
        },
        function func2(err, value) {
            // ...
            return value
        },
        function funcFinal(err, value) {
            if (err) throw err;
        // ...
        }
    );
    
  • Q.fcall(func1)
        .then(func2)
        .then(func3)
        .then(funcSucces, funcError)
    
于 2013-01-15T13:41:45.990 回答