如何在多个其他函数完成后才执行Javascript函数?

我的具体问题是,我需要执行(可能)大量的Javascript函数来准备像batch file(每个函数调用添加一些信息到同一个batch file),然后,所有这些调用完成后,执行一个最终函数发送batch file(比如说,把它作为HTML响应发送)。 我正在寻找一个通用的JavaScript编程模式。

泛化问题:给定Javascript函数funcA(),funcB()和funcC(),我会找出命令执行的最佳方法,以便funcC和funcB执行后才执行funcC。 我知道我可以使用这样的嵌套callback函数:

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

我甚至可以通过传递callback参数来使这个模式更加普遍,但是这个解决scheme变得非常冗长。

我也熟悉Javascript函数链接,解决scheme可能如下所示:

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

虽然这个解决scheme似乎对我来说更简洁一些,但是当您为计算添加更多步骤时,函数执行链会变得越来越长。

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

谢谢你的帮助。

另一个想法:也许传递一个共享对象funcA和funcB,当他们完成执行标记共享对象像sharedThing.funcA =“完整”或sharedThing.funcB =“完成”,然后以某种方式? 在共享对象达到所有字段标记完成的状态时执行funcC。 我不确定你可以怎么做funcC等待这个。

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

如果你想保持简单,你可以使用基于计数器的callback系统。 这是一个允许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(); } }); } } }; }; 

用法:

 when( function(done) { // do things done(); }, function(done) { // do things setTimeout(done, 1000); }, ... ).then(function() { // all are done }); 

如果您不使用任何asynchronous函数,并且您的脚本不会破坏执行的顺序,那么最简单的解决scheme就像Pointy和其他人所说的那样:

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

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

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

你可以通过忽略标识符str来简化这个对象,只是增加/减lessthis._count ,但是这个系统对于debugging是有用的。

为了使用call_after_completion ,只需使用所需的函数func作为参数和add_condition来创build一个new call_after_completion 。 只有在所有条件都被删除的情况下才会调用func

例:

 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演示

如果你不想使用任何助手库,你需要自己编写一些助手,没有简单的一行解决scheme。

如果你想以一种看起来像在同步的情况下看起来一样可读的方式结束,可以尝试一些延迟/承诺的概念实现(它仍然是普通的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); 

看看jQuery的延期对象。 这提供了一个复杂的手段来控制在asynchronous环境中发生的事情。

AJAX这个明显的用例就是AJAX,但不限于此。

资源:

  • jQuery文档:延期对象
  • 很好地介绍了延迟对象模式
  • 非AJAX用于jQuery的延期对象

我正在寻找同样的模式。 我正在使用询问多个远程数据源的API。 每个API都要求我将callback函数传递给它们。 这意味着我不能只是开始一套我自己的职能,等待他们回来。 相反,我需要一个解决scheme,使用一组可能以任何顺序调用的callback,具体取决于不同数据源的响应程度。

我想出了以下解决scheme。 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' ) ); 

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

 how about: funcC(funcB(funcA))); 

我认为这些问题是因为一些函数运行时间较长,当funcA或funcB没有执行时,运行funcC可能会出现这种情况。