为javascript建立方便的回调编写风格



回调在编码中越来越重要,特别是当您考虑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);

但是有很多变量一直在传递。由于中间写了其他函数,这就很难读了。函数本身可能太不重要,不值得给它们单独的文件。

使用异步流控制库,如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]));
    }  
});

这将依次执行每个函数;一旦第一个调用了它的回调,第二个就会被调用,以此类推。但是你也可以用parallel或者waterfall或者任何你想要的流。

另一种回调方法是承诺。

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

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

但美元。Ajax也返回一个承诺。

var request = $.ajax({
  url: '/foo'
});
request.done(function() {
    alert('bar');
});
一个好处是,您可以模拟同步行为,因为您可以使用返回的承诺,而不是为$.ajax提供回调。成功和回调,回调和回调....另一个优点是,您可以链接/聚合承诺,并且如果您愿意,可以为一个承诺聚合设置错误处理程序。

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

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

结合答案和文章。为了大家的利益,请编辑这个答案并以直接的方式添加libraries/examples/doc-urls。

关于承诺的文档

    带有承诺的异步控制流
  • jQuery延迟
<

异步库/strong>

  • async.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)
    
    • API参考
    • 模式示例
    • 更多文档

最新更新