如何从此casperjs代码中删除堆栈溢出(使用setTimeout)?

时间:2014-07-08 16:30:22

标签: javascript settimeout casperjs

以下示例类似于我的实际代码:

function runCode() {
    casper.then(function(){
        if (condition){
            return;
        }
    });

    .... code .....
    .... code .....

    casper.then(function(){
        setTimeout(runCode(), 1000);
    });
}

function startScript() {
    .... code ....
    .... code ....

    casper.then(function(){
        runCode();
    });

    casper.then(function(){
        setTimeout(startScript(),5000);
    });
}

startScript();

此代码在vps上运行,似乎填满了所有512 MB的RAM。它最初以大约50 MB RAM开始,并在几个小时内继续填充它。所以我怀疑我实现无限循环的方式是创建新的堆栈帧而不破坏旧的堆栈帧。

我希望如何实现这一点:执行从startScript()开始,在startScript()内部调用另一个函数runCode()。这个runCode函数必须在循环中无限运行。我正在尝试使用setTimeout函数。

有一个条件在达到整个脚本再次启动时所以我正在使用return并返回startScript()函数,然后使用另一个setTimeout()函数重新启动它。

在过去的几个小时里,我的脚本中没有遇到我正在谈论的具体情况。所以,我怀疑内存使用是在runCode()函数内。请给我一些建议,以消除此内存使用问题。

更新: 我发送函数的返回值(为null或未定义)作为setTimeout()的参数,为此函数必须运行一次,这导致stackoverflow。正如Artjom B.所建议的那样,我尝试了以下代码,但是没有调用作为参数传递给setTimeout的函数。

function runCode() {

    console.log("inside runcode");
    casper.then(function(){
    ...
    ...
    // call to other functions
    });

    //setTimeout(runCode, 1000); --------------- [i]

    casper.then(function(){
        console.log("just before setTimeout");
        setTimeout(runCode, 1000);
    });
}
runCode();

我得到以下输出:

inside runcode console.log messages from the other functions and codes in between. just before setTimeout 然后它退出。

如果我使用 [i] 指示的注释掉的代码,并在此之后注释掉这些行。我得到一个像这样的无限循环: inside runcode inside runcode inside runcode .... .... 我不知道出了什么问题。请给我一些建议。

更新2:感谢您Artjom B.提取another flaw in my codesetTimeout()函数似乎有问题。当我在此粘贴中运行代码:http://pastebin.com/W9DD6YpB时,它似乎无法按预期运行。

更新3:正如Artjom B.所解释的那样,javascript的异步性质导致casper认为没有更多的代码可以执行,所以它在由setTimeout排队的函数之前退出被调用。 我想知道是否添加一些代码后会使casper不退出。例如,由setTimeout()排队的函数等待1000毫秒被调用。所以,casper.wait(2000)应该做的工作,但我不知道是否仍然存在堆栈溢出问题:http://pastebin.com/ybKWH5KX

1 个答案:

答案 0 :(得分:2)

在评论中进行了一些讨论之后,我们明确表示setTimeout的方法不起作用或者难以阅读和维护。

堆叠帧

由于CasperJS内部使用runCode,因此您对startScriptsetTimeout的递归调用中未收集的堆栈帧的关注尚未解决。所以你应该使用CasperJS提供的功能。

您可以递归地执行此操作(嵌套步骤),因为CasperJS使用队列处理此问题并在当前执行的步骤之后插入新步骤。

停止条件

您需要将停止条件移动到递归调用,因为在这样的异步代码中

function runCode() {
    casper.then(function(){
        if (condition){
            return;
        }
    });
    //...
}

实际上并没有停止runCode执行,因为它只是从then块内部的函数返回。

替换setTimeout

然后您将替换setTimeout

function runCode() {
    //...
    casper.then(function(){
        if (!condition){
            setTimeout(runCode, 1000);
        }
    });
}

使用正确的casper函数:

function runCode() {
    //...
    casper.wait(1000);
    casper.then(function(){
        if (!condition){
            runCode();
        }
    });
}

您需要在startScript中执行相同的替换:

casper.then(function(){
    setTimeout(startScript,5000);
});

casper.wait(5000);
casper.then(function(){
    startScript();
});

保持setTimeout

如果您真的想保留setTimeout,那么您需要执行双重记账。通过使用setTimeout调用函数,您可以打破casper步骤的受控流程。

例如,你可以这样做:

function someFunction(){
    casper.then(function(){
        // something
    });
}
casper.start(url);
casper.then(function(){
    setTimeout(someFunction, 5000);
});
casper.run();

then内的功能实际上是最后一个计划步骤。当它被执行时,它将创建一个计时器,然后启动一个函数,这反过来将为流添加更多的步骤。这种情况永远不会发生,因为casper无法知道是否会安排更多步骤,而且由于目前还没有(在then run之前的setTimeout结尾),它将只是退出完整的脚本。虽然在某些平台上,底层幻像可能表现不同。 function someFunction(){ casper.then(function(){ // something }); } casper.start(url); casper.then(function(){ setTimeout(someFunction, 5000); }); casper.wait(5100); // should be greater than the previous timeout casper.run(); 可让您突破控制流程。在这种情况下,这可能不太好。

要获得控制权,您可以按照paste

中的说明执行以下操作
casper.start(url);
casper.then(function(){
    // something
});
casper.wait(5000, someFunction); // added bonus because "this" now refers to casper
casper.run();

^不要这样做。难以阅读且容易出错。这可以简化为:

setTimeout

setTimeout

的正确回调调用

您在setTimeout中实际调用函数时也遇到语法问题。主要问题是您实际上并未使用setTimeout(startScript(),5000); 。例如,参见

startScript

由于(),您可以立即调用setTimeout函数,并将返回值传递给startScript函数。我不认为你真的从setTimeout返回任何东西。 undefined会在不发出警告或错误的情况下使用(),但在超时后无法执行,因为它实际上并不是一个功能。在javascript函数中是一等公民。您可以将函数对象传递给其他函数。

您可以通过从上一行删除setTimeout(startScript,5000); 来解决此问题:

setTimeout(runCode, 1000);

同样如此
then*

(未经测试)删除先前的casper步骤的解决方案

你真的应该从cron运行脚本而不需要递归或类似的东西。如果你真的不想这样,你仍然可以减少内存消耗。

通过wait*casper.steps和其他一些计划的步骤在internal casper.clearSomeSteps = function(min, keep){ var len = casper.steps.length; min = min || 1000; // only run when at least 1000 steps are scheduled keep = keep || 100; // keep 100 of the newer steps if (len < min) return; // not yet needed this.step -= len-keep; // change the index of the current step this.steps = Array.prototype.slice.call(this.steps, len-keep); // do the slice }; 媒体资源中进行管理。一旦执行它们就不会被清除。这可能是你的内存泄漏的原因。您可以尝试像这样清除它们:

this.clearSomeSteps()

startScript开头致电{{1}}。虽然这可能不是整个解决方案,因为还有casper.waiters