in javascript, what are best practices for executi

2019-07-02 12:15发布

having a function that needs to call an indeterminate number of other (potentially asynchronous) functions, each with callback, I am using the following pattern.

While pretty certain it is correct, not very pleasing aesthetically.

// 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;
        }
}

To me it seems necessary to take two steps: the first to see how many funcs will be called, and the second to do the calling. But perhaps my brain is addled from old-school programming or having read too much Lear last night.

2条回答
冷血范
2楼-- · 2019-07-02 12:38

The best management idea I've seen for multiple aysnc functions is Deferreds and Futures as described in this Script Junkie article, this article about the FutureJS implementation, this msdn article and this Stack Overflow question.

It's actually a structured way of thinking about sequencing multiple asynchronous calls or defining execution dependencies between them and it seems to have traction with multiple implementations underway (already usable) for different frameworks so it seems like a generally supported idea that will be useful to learn both now and in the future.

Managing multiple async calls with dependencies between them is indeed messy by hand. Not only does the code look messy, but it's impossible to read and even more impossible to use a debugger on. Debugging typically requires dumping lots of info to a log file and then trying to sort out what happened. If timing is involved, then you're really in for a mess. In my last project, the only area that I still have an unresolved bug is some sort of missed dependency between four different async calls at startup. I've fortified the code to make it so the error almost never happens, but it isn't quite gone yet. Next step is to switch to using deferreds and add some formal structure to it.

As for some of the bigger libraries, we have jQuery Defereds, YUI3 has Async Queue (which is less powerful than general deferreds, but useful), Dojo has a Deferred object and there are a couple deferred libraries that aren't tied to a main library.

查看更多
欢心
3楼-- · 2019-07-02 12:51

I have an after utility function.

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); });
}

This should be used for simple cases. For more complex situations use some form of flow control like Futures

查看更多
登录 后发表回答