在javascript中,使用回调执行多个异步函数的最佳实践是什么?

时间:2011-07-29 07:24:23

标签: javascript syntax asynchronous callback

有一个函数需要调用不确定数量的其他(可能是异步的)函数,每个函数都有回调,我使用以下模式。

虽然很确定它是正确的,但不是很美观。

// fn responsible for figuring out and calling aysnc funcs
function fn( arg, outNext ) {

        // calculate how many funcs we will be calling
        var waitCt = 0;
        var a, b, c;
        if( a = arg['a'] ) waitCt++;
        if( b = arg['b'] ) waitCt++;
        if( c = arg['c'] ) waitCt++;

        // call funcs
        if( a ) fnArbitrary( arg.a, inNext );
        if( b ) fnRandom( arg, inNext );
        if( c ) fnClueless( 15, inNext );

        // calback func
        function inNext( err ) {

                // wait for one less func
                waitCt--;

                // return err if found
                if( err ) outNext( err );

                // return nada if all funcs performed
                if( waitCt == 0 ) outNext();

                // otherwise we're waiting for other funcs to finish
                return;
        }
}

对我来说,似乎有必要采取两个步骤:第一步看到将调用多少个func,第二个进行调用。但也许我的大脑是从老派的节目中汲取的,或者昨晚读了太多的李尔。

2 个答案:

答案 0 :(得分:3)

我见过的多个aysnc函数的最佳管理理念是in this Script Junkie articlethis article about the FutureJS implementationthis msdn articleStack Overflow question所述的延迟和期货。

它实际上是一种考虑排序多个异步调用或定义它们之间的执行依赖关系的结构化方式,它似乎对不同框架正在进行的多个实现(已经可用)具有牵引力,因此它似乎是一个普遍支持的想法,它将是有用的现在和将来都要学习。

管理多个异步调用以及它们之间的依赖关系确实很麻烦。代码不仅看起来很乱,而且无法读取,甚至更不可能使用调试器。调试通常需要将大量信息转储到日志文件中,然后尝试解决发生的事情。如果涉及时间安排,那么你真的很乱。在我的上一个项目中,我仍然有一个尚未解决的bug的唯一区域是在启动时四种不同异步调用之间的某种错过的依赖关系。我已经强化了代码来实现它,所以错误几乎从未发生过,但它还没有完全消失。下一步是切换到使用延迟并为其添加一些正式结构。

对于一些较大的库,我们有jQuery Defereds,YUI3有Async Queue(它比一般的延迟更强大,但很有用),Dojo有Deferred object并且有一对与主库无关的延迟库。

答案 1 :(得分:1)

我有一个后效用函数。

var after = function _after(count, f) {
  var c = 0, results = [];
  return function _callback() {
    switch (arguments.length) {
      case 0: results.push(null); break;
      case 1: results.push(arguments[0]); break;
      default: results.push(Array.prototype.slice.call(arguments)); break;
    }
    if (++c === count) {
      f.apply(this, results);
    }
  };
};

// fn responsible for figuring out and calling aysnc funcs
function fn( arg, outNext ) {

    var cb = after(Object.keys(arg).length, function(data) {
      // all finished. Do something
    });

    // replace fa, fb, fc with f['a'], f['b'], f['c']
    Object.keys(arg).forEach(function(k) { f[k](arg[k], cb); });
}

这应该用于简单的情况。对于更复杂的情况,请使用某种形式的流控制,如Futures