2

我有一个 javascript-Function 问题,我无法使用回调函数。

该函数获取“命令”列表,并对其进行迭代。它一个接一个地执行一个命令。为此,我必须使用带有回调的递归函数。

这是一个描述它的例子:

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 测试过,你可能需要在其他浏览器中增加 Array(IE 有一个巨大的 Callstack)。

编辑2:

感谢您的帮助,我不知道添加/删除超时之间的区别。悬停,它不能解决我的问题。这里有更多细节: 我有不同的命令。有些命令是同步的,有些是异步的。所以我必须使用回调函数,但调用堆栈仍然存在问题。

这是一个更新的示例:

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

还有小提琴:http: //jsfiddle.net/B7APC/

4

3 回答 3

2
// setTimeout(function(){
    console.log("Processed command " + command);
    cb();
// }, 1);

这就是原因。如果没有setTimeout,您很容易达到调用堆栈限制(带有命名函数的演示):

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
于 2014-03-05T16:12:26.897 回答
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();
}
于 2014-03-05T14:25:42.227 回答
0

听起来像是promise的完美案例。tbh 我还没有尝试过它们,但它们在我的列表的顶部。看看测序。此外,promise 中的操作是否同步也无关紧要。

所以你会有类似的东西

commands.forEach(function(cmd) {
    execCmd(cmd).then(onSuccess, onFailure);
});

对不起,这不是很具体,但你必须做更多的研究。

于 2014-07-08T12:09:11.840 回答