我的具体问题是我需要执行(可能)大量的Javascript函数来准备像批处理文件(每个函数调用将一些信息添加到同一个批处理文件),然后,在所有这些调用完成之后,执行最终函数以发送批处理文件(例如,将其作为HTML响应发送)。我正在寻找一个通用的Javascript编程模式。
概括问题: 鉴于Javascript函数funcA(),funcB()和funcC(),我想找出订购执行的最佳方法,以便funcC仅在执行funcA和funcB之后执行。我知道我可以使用这样的嵌套回调函数:
funcA = function() {
//Does funcA stuff
funcB();
}
funcB = function() {
//Does funcB stuff
funcC();
}
funcA();
我甚至可以通过传入回调参数使这个模式更加通用,但是,这个解决方案变得非常冗长。
我也熟悉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();
虽然这个解决方案对我来说似乎更清晰,但随着您向计算添加更多步骤,函数执行链变得越来越长。
对于我的具体问题,执行funcA,funcB等的顺序无关紧要。因此,在我上面的解决方案中,我在技术上做了比所需更多的工作,因为我将所有功能放在一个串行排序中。对我来说最重要的是funcC(用于发送结果或触发请求的某些函数)仅在funcA和funcB完成所有执行后调用。理想情况下,funcC可以某种方式监听所有中间函数调用完成,然后执行?我希望学习一般的Javascript模式来解决这个问题。
感谢您的帮助。
另一个想法: 也许将共享对象传递给funcA和funcB,当它们完成执行时,标记共享对象,如sharedThing.funcA =“complete”或sharedThing.funcB =“complete”然后以某种方式?当共享对象到达所有字段都标记为完成的状态时执行funcC。我不确定你究竟能让funcC等到这个。
编辑: 我应该注意到我正在使用服务器端Javascript(Node.js),我想学习一种模式来解决它只是使用普通的旧Javascript(不使用jQuery或其他库)。当然这个问题很普遍,有一个干净的纯Javascript解决方案吗?
答案 0 :(得分:7)
如果您想保持简单,可以使用基于计数器的回调系统。这是允许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
});
答案 1 :(得分:1)
如果您不使用任何异步函数并且您的脚本不会破坏执行顺序,那么最简单的解决方案就是Pointy和其他人所说:
funcA();
funcB();
funcC();
但是,由于您正在使用node.js,我相信您将使用异步函数并希望在异步IO请求完成后执行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
并仅增加/减少this._count
来简化此对象,但此系统可用于调试。
要使用call_after_completion
,您只需创建一个new call_after_completion
,其中包含您所需的函数func
作为参数和add_condition
s。只有在删除所有条件后才会调用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);
答案 2 :(得分:1)
如果您不想使用任何帮助程序库,那么您需要自己编写一些帮助程序,对此没有简单的一行解决方案。
如果您希望以与同步情况相似的方式结束某些内容,请尝试一些延迟/保证概念实现(它仍然是纯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);
答案 3 :(得分:0)
查看jQuery的延迟对象。这提供了一种控制异步环境中发生情况的复杂方法。
明显的用例是AJAX,但并不局限于此。
资源:
答案 4 :(得分:0)
我一直在寻找同样的模式。我正在使用API来询问多个远程数据源。每个API都要求我将回调函数传递给它们。这意味着我不能只发射一组自己的函数并等待它们返回。相反,我需要一个适用于一组回调的解决方案,这些回调可以按任何顺序调用,具体取决于不同数据源的响应速度。
我提出了以下解决方案。 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说,&#34;我正在为此寻找一般的Javascript编程模式,&#34;所以我发布了这个,即使我没有使用节点。
答案 5 :(得分:0)
如今,人们可以做这样的事情:
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);
});
var promise = (
new Promise(async resolve => resolve( await funcA() ))
).then(result_a => funcB(result_a)).then(result_b => funcC(result_b));
promise.then(result_c => console.log('done.'));
答案 6 :(得分:-1)
how about:
funcC(funcB(funcA)));
我认为问题是因为某些函数运行时间更长,当funcA或funcB没有执行时,我们运行funcC可能会出现这种情况。