如何仅在多个其他功能完成后才执行 Javascript 功能?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11278018/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 05:15:17  来源:igfitidea点击:

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

javascriptnode.jsdesign-patternsasynchronous

提问by dgh

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 information to the same batch file) and then, after all those calls are completed, execute a final function to send the batch file (say, send it as an HTML response). I'm looking for a general Javascript programming pattern for this.

我的具体问题是我需要执行(可能)大量的 Javascript 函数来准备像批处理文件这样的东西(每个函数调用都会向同一个批处理文件添加一些信息),然后在所有这些调用完成后,执行一个发送批处理文件的最终函数(例如,将其作为 HTML 响应发送)。我正在为此寻找通用的 Javascript 编程模式。

Generalize problem: Given the Javascript functions funcA(), funcB(), and funcC(), I would to figure out the best way to order execution so that funcC is only executed after after funcA and funcB have executed. I know that I could use nested callback functions like this:

概括问题:鉴于 Javascript 函数 funcA()、funcB() 和 funcC(),我想找出排序执行的最佳方法,以便 funcC 仅在 funcA 和 funcB 执行后执行。我知道我可以使用这样的嵌套回调函数:

funcA = function() {
    //Does funcA stuff
    funcB();
}
funcB = function() {
    //Does funcB stuff
    funcC();
}

funcA();

I could even make this pattern a little more general by passing in callback parameters, however, this solution becomes quite verbose.

我什至可以通过传入回调参数使这个模式更通用,但是,这个解决方案变得非常冗长。

I am also familiar with Javascript function chaining where a solution might look like:

我也熟悉 Javascript 函数链,其中解决方案可能如下所示:

myObj = {}
myObj.answer = ""
myObj.funcA = function() {
    //Do some work on this.answer
    return this;
}
myObj.funcB = function() {
    //Do some more work on this.answer
    return this;
}
myObj.funcC = function() {
    //Use the value of this.answer now that funcA and funcB have made their modifications
    return this;
}
myObj.funcA().funcB().funcC();

While this solution seems a little cleaner to me, as you add more steps to the computation, the chain of function executions grows longer and longer.

虽然这个解决方案对我来说似乎更清晰一些,但随着您向计算添加更多步骤,函数执行链会变得越来越长。

For my specific problem, the order in which funcA, funcB, etc. are executed DOES NOT matter. So in my solutions above, I am technically doing more work than is required because I am placing all the functions in a serial ordering. All that matters to me is that funcC (some function for sending the result or firing off a request) is only called after funcA and funcB have ALL completed execution. Ideally, funcC could somehow listen for all the intermediate function calls to complete and THEN would execute? I hoping to learn a general Javascript pattern to solve such a problem.

对于我的具体问题,funcA、funcB 等的执行顺序无关紧要。所以在我上面的解决方案中,我在技术上做了比需要更多的工作,因为我将所有功能放在一个串行排序中。对我来说最重要的是 funcC(一些用于发送结果或触发请求的函数)仅在 funcA 和 funcB 完成执行后才被调用。理想情况下,funcC 可以以某种方式侦听所有要完成的中间函数调用,然后执行?我希望学习一个通用的Javascript模式来解决这样的问题。

Thanks for your help.

谢谢你的帮助。

Another Idea: Maybe pass a shared object to funcA and funcB and when they complete execution mark the shared object like sharedThing.funcA = "complete" or sharedThing.funcB = "complete" and then somehow? have funcC execute when the shared object reaches a state where all fields are marked complete. I'm not sure how exactly you could make funcC wait for this.

另一个想法:也许将共享对象传递给 funcA 和 funcB,当它们完成执行时,将共享对象标记为 sharedThing.funcA = "complete" 或 sharedThing.funcB = "complete" 然后以某种方式?当共享对象达到所有字段都标记为完成的状态时,让 funcC 执行。我不确定你究竟如何让 funcC 等待这个。

Edit: I should note that I'm using server-side Javascript (Node.js) and I would like to learn a pattern to solve it just using plain old Javascript (without the use of jQuery or other libraries). Surely this problem is general enough that there is a clean pure-Javascript solution?

编辑:我应该注意到我正在使用服务器端 Javascript (Node.js),我想学习一种模式来解决它,只使用普通的旧 Javascript(不使用 jQuery 或其他库)。这个问题肯定足够普遍,有一个干净的纯 Javascript 解决方案吗?

采纳答案by pimvdb

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/thenis actually just sugar, but then again the whole system arguably is.)

如果您想保持简单,可以使用基于计数器的回调系统。这是一个允许when(A, B).then(C)语法的系统草案。(when/then实际上只是糖,但是整个系统又可以说是。)

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

回答by Zeta

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:

如果您不使用任何异步函数并且您的脚本没有破坏执行顺序,那么最简单的解决方案是,如 Pointy 和其他人所述:

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

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

但是,由于您使用的是 node.js,我相信您将使用异步函数并希望funcC在异步 IO 请求完成后执行,因此您必须使用某种计数机制,例如:

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 strand just increasing/decreasing this._count, however this system could be useful for debugging.

您可以通过忽略标识符str并只是增加/减少来简化这个对象this._count,但是这个系统对于调试很有用。

In order to use call_after_completionyou simply create a new call_after_completionwith your desired function funcas argument and add_conditions. funcwill only be called if all conditions have been removed.

为了使用call_after_completion你只需创建一个new call_after_completion你想要的函数func作为参数和add_conditions。func仅当所有条件都已删除时才会调用。

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

JSFiddle 演示

回答by Mariusz Nowak

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 deferredpackage you may end up with something as simple as:

如果您想以看起来像在同步情况下一样可读的东西结束,请尝试一些延迟/承诺概念实现(它仍然是纯 JavaScript),例如使用deferred包,您最终可能会得到一些简单的东西:

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

回答by crantok

I was looking for the same kind of pattern. I am using APIs that interrogate multiple remote data sources. The APIs each require that I pass a callback function to them. This means that I cannot just fire off a set of my own functions and wait for them to return. Instead I need a solution that works with a set of callbacks that might be called in any order depending on how responsive the different data sources are.

我正在寻找相同的模式。我正在使用查询多个远程数据源的 API。每个 API 都要求我将回调函数传递给它们。这意味着我不能只是触发一组我自己的函数并等待它们返回。相反,我需要一个解决方案来处理一组回调,这些回调可以根据不同数据源的响应程度以任何顺序调用。

I came up with the following solution. JS is way down the list of languages that I am most familiar with, so this may not be a very JS idiom.

我想出了以下解决方案。JS 在我最熟悉的语言列表中排在后面,所以这可能不是一个非常 JS 的习惯用法。

function getCallbackCreator( number_of_data_callbacks, final_callback ) {

    var all_data = {}

    return function ( data_key ) {

        return function( data_value ) {
            all_data[data_key] = data_value;

            if ( Object.keys(all_data).length == number_of_data_callbacks ) {
                final_callback( all_data );
            }
        }
    }
}

var getCallback = getCallbackCreator( 2, inflatePage );

myGoogleDataFetcher( getCallback( 'google' ) );
myCartoDataFetcher( getCallback( 'cartodb' ) );

Edit: The question was tagged with node.js but the OP said, "I'm looking for a general Javascript programming pattern for this," so I have posted this even though I am not using node.

编辑:这个问题用 node.js 标记,但 OP 说,“我正在为此寻找一个通用的 Javascript 编程模式”,所以即使我没有使用 node,我也发布了这个。

回答by Utkanos

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

看看 jQuery 的延迟对象。这提供了一种控制在异步环境中发生的情况的复杂方法。

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

明显的用例是 AJAX,但它不仅限于此。

Resources:

资源:

回答by A. K.

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.

我认为问题是因为某些函数运行时间更长,并且当 funcA 或 funcB 没有完成执行时,我们运行 funcC 时可能会出现这种情况。