我遇到了javascript-Function的问题,我无法使用callback-Functions。
该函数获取一个“命令”列表,并对它们进行迭代。它执行一个接一个的命令。 为此,我必须使用带回调的递归函数。
以下是一个描述它的示例:
function processList( inputArray, cb ){
if(inputArray.length == 0) return cb(); //No commands left
var command = inputArray.pop(); //Get last Element from the List
processCommand(command, function(){ //Execute the command
return processList(inputArray, cb); //Continue with the next command, when this one finished
});
}
function processCommand(command, cb){
setTimeout(function(){
console.log("Processed command "+command);
cb();
}, 10);
}
这就是我调用函数的方式:
processList( ["cmd1", "cmd2", "cmd3"], function(){
console.log("DONE");
});
它完美地运行,一个命令在前一个命令之后执行。
我的问题:
该列表包含数千个元素,甚至可能列表在处理过程中获取新命令。我会在几秒钟内达到最大的呼叫堆栈。
我不需要调用堆栈。当我完成最后一个命令时,只有数千个返回,它引导我回到函数,它启动了所有内容。
我不知道如何解决这个问题,我不想使用忙等待(这会使代码效率极低)。
还有其他伎俩吗?喜欢信号还是捣乱呼叫堆栈?
修改
这是一个用于演示的jsFiddle:http://jsfiddle.net/db6J8/ (请注意,您的浏览器标签可能会冻结/崩溃)
错误消息为Uncaught RangeError: Maximum call stack size exceeded
我使用Chrome测试过,您可能需要在其他浏览器中增加数组(IE有一个巨大的Callstack)。
EDIT2:
感谢您的帮助,我没有意识到添加/删除超时之间的区别。悬停,它不能解决我的问题。 以下是一些更多细节: 我有不同的命令。有些命令是同步的,有些则是异步的。所以我必须使用callback-Functions,但仍然存在callstack的问题。
这是一个更新的例子:
var commands = [];
for(var i=15000; i>=0;i--){ commands.push(i); }
processList(commands, function(){
alert("DONE");
});
function processList( inputArray, cb ){
if(inputArray.length == 0) return cb(); //No commands left
var command = inputArray.pop(); //Get last Element from the List
processCommand(command, function(){ //Execute the command
return processList(inputArray, cb); //Continue with the next command, when this one finished
});
}
function processCommand(command, cb){
if(command%2 == 0){ //This command is synchron
console.log("Processed sync. command "+command);
cb();
}else{
setTimeout(function(){
console.log("Processed async. command "+command);
cb();
}, 1);
}
}
答案 0 :(得分:2)
// setTimeout(function(){ console.log("Processed command " + command); cb(); // }, 1);
这就是原因。如果没有setTimeout
,您很容易达到调用堆栈限制(Demo with named functions):
Maximum recursion depth exceeded:
finishedCommand _display/:16
processCommand _display/:23
processList _display/:15
finishedCommand _display/:16
processCommand _display/:23
…
processList _display/:15
finishedCommand _display/:16
processCommand _display/:23
processList _display/:15
<Global Scope> _display/:16
选择同步循环而不是递归调用processCommand
。
如果你让setTimeout
生效,每个计时器事件都会用一个全新的调用堆栈调用该函数,它永远不会溢出:
setTimeout(function later(){
console.log("Processed command " + command);
cb();
}, 1);
当您记录“DONE”时,您会看到堆栈始终如此 - 无论您处理了多少命令(Demo):
BreakPoint at console.log()
readyHandler show/:8
processList show/:13
finishedCommand show/:17
later show/:24
<Global Scope> // setTimeout
答案 1 :(得分:1)
我认为这样的事情可能不会保留在调用堆栈上,因为它不会将回调作为参数。
function processList( inputArray, cb ){
var processCommand = function(){
if(inputArray.length == 0) cb(); //No commands left
var command = inputArray.pop();
setTimeout(function(){
console.log("Processed command "+command);
processCommand();
}, 10);
};
processCommand();
}
答案 2 :(得分:0)
听起来像是promises的完美案例。 tbh我还没有尝试过,但它们在我的列表中排名第一。 在sequencing闲聊。此外,承诺中的操作是否同步无关紧要。
所以你有类似
的东西commands.forEach(function(cmd) {
execCmd(cmd).then(onSuccess, onFailure);
});
对不起,这不是很具体,但你必须做更多的研究。