How to execute a Javascript function only after multiple other functions have completed?

后端 未结 7 1994
轻奢々
轻奢々 2020-12-25 15:03

My specific problem is that I need to execute a (potentially) large number of Javascript functions to prepare something like a batch file (each function call adds some infor

相关标签:
7条回答
  • 2020-12-25 15:39

    If you don't use any asynchronous functions and your script doesn't break the order of execution, then the most simple solution is, as stated by Pointy and others:

    funcA(); 
    funcB();
    funcC();
    

    However, since you're using node.js, I believe you're going to use asynchronous functions and want to execute funcC after a async IO request has finished, so you have to use some kind of counting mechanisms, for example:

    var call_after_completion = function(callback){
        this._callback = callback;
        this._args = [].slice.call(arguments,1);
        this._queue = {};
        this._count = 0;
        this._run = false;
    }
    
    call_after_completion.prototype.add_condition = function(str){
        if(this._queue[str] !== undefined)
            throw new TypeError("Identifier '"+str+"' used twice");
        else if(typeof str !== "String" && str.toString === undefined)
            throw new TypeError("Identifier has to be a string or needs a toString method");
    
        this._queue[str] = 1;
        this._count++;
        return str;
    }
    
    call_after_completion.prototype.remove_condition = function(str){
        if(this._queue[str] === undefined){
            console.log("Removal of condition '"+str+"' has no effect");
            return;
        }
        else if(typeof str !== "String" && str.toString === undefined)
            throw new TypeError("Identifier has to be a string or needs a toString method");
    
        delete this._queue[str];
    
        if(--this._count === 0 && this._run === false){
            this._run = true;
            this._callback.apply(null,this._args);
        }
    }
    

    You can simplify this object by ignoring the identifier str and just increasing/decreasing this._count, however this system could be useful for debugging.

    In order to use call_after_completion you simply create a new call_after_completion with your desired function func as argument and add_conditions. func will only be called if all conditions have been removed.

    Example:

    var foo = function(){console.log("foo");}
    var bar = new call_after_completion(foo);
    var i;
    
    bar.add_condition("foo:3-Second-Timer");
    bar.add_condition("foo:additional function");
    bar.add_condition("foo:for-loop-finished");
    
    function additional_stuff(cond){
        console.log("additional things");
        cond.remove_condition("foo:additional function");
    }
    
    for(i = 0; i < 1000; ++i){
    
    }
    console.log("for loop finished");
    bar.remove_condition("foo:for-loop-finished");
    additional_stuff(bar);
    
    setTimeout(function(){
        console.log("3 second timeout");
        bar.remove_condition("foo:3-Second-Timer");
    },3000);
    

    JSFiddle Demo

    0 讨论(0)
  • 2020-12-25 15:49

    Nowadays, one can do something like this:

    Let's say we have both funcA, funcB and funcC:

    If one's want funcA and funcB results to be passed to funcC:

    var promiseA = new Promise((resolve, reject) => {
      resolve(await funcA());
    });
    var promiseB = new Promise((resolve, reject) => {
      resolve(await funcB());
    });
    var promise = Promise.all([ promiseA, promiseB ]).then(results => {
      // results = [result from funcA, result from funcB]
      return funcC(results);
    });
    

    If one's want funcA, then funcB and then funcC:

    var promise = (
      new Promise(async resolve => resolve( await funcA() ))
    ).then(result_a => funcB(result_a)).then(result_b => funcC(result_b));
    

    And finally:

    promise.then(result_c => console.log('done.'));
    
    0 讨论(0)
  • 2020-12-25 15:50

    If you want to keep it simple, you can use a counter-based callbacks system. Here's a draft of a system that allows when(A, B).then(C) syntax. (when/then is actually just sugar, but then again the whole system arguably is.)

    var when = function() {
      var args = arguments;  // the functions to execute first
      return {
        then: function(done) {
          var counter = 0;
          for(var i = 0; i < args.length; i++) {
            // call each function with a function to call on done
            args[i](function() {
              counter++;
              if(counter === args.length) {  // all functions have notified they're done
                done();
              }
            });
          }
        }
      };
    };
    

    Usage:

    when(
      function(done) {
        // do things
        done();
      },
      function(done) {
        // do things
        setTimeout(done, 1000);
      },
      ...
    ).then(function() {
      // all are done
    });
    
    0 讨论(0)
  • 2020-12-25 15:54

    Have a look into jQuery's deferred objects. This provides a sophisticated means of controlling what happens when in an asynchronous environment.

    The obvious use-case for this is AJAX, but it is not restricted to this.

    Resources:

    • jQuery docs: deferred object
    • good introduction to deferred object patterns
    • Non-AJAX use for jQuery's deferred objects
    0 讨论(0)
  • 2020-12-25 15:57

    If you don't want to use any helper libraries, than you need to write some helper yourself, there's no simple one line solution for this.

    If you'd like to end with something that looks as readable as it would in synchronous case, try some deferred/promise concept implementation (it's still plain JavaScript), e.g. using deferred package you may end up with something as simple as:

    // Invoke one after another:
    funcA()(funcB)(funcC);
    
    // Invoke funcA and funcB simultaneously and afterwards funcC:
    funcA()(funcB())(funcC);
    
    // If want result of both funcA and funcB to be passed to funcC:
    deferred(funcA(), funcB())(funcC);
    
    0 讨论(0)
  • 2020-12-25 16:00
    how about:
    funcC(funcB(funcA)));
    

    I think the questions is because some of functions run longer and there might be a situation when we run funcC when funcA or funcB did not fininsh executing.

    0 讨论(0)
提交回复
热议问题