【问题标题】:Efficient call stack break technique高效的调用堆栈中断技术
【发布时间】:2014-09-06 16:54:32
【问题描述】:

场景

我有一个工作人员处理项目并通过处理程序函数调用传播结果。

代码

在某些时候,Worker 在processNextItem() 调用和resultHandler() 回调之间在内部调用异步方法,这会重置调用堆栈:

function Worker(resultHandler) {
    this.processNextItem = function () {
        // some work
        ...

        // propagate result
        resultHandler(someResult);
    }
}   

var myResultHandler = function (result) {
    // Process result
    ...

    // Process next item
    worker.processNextItem();
};

var worker = new Worker(myResultHandler);

// Start working on first item
Worker.processNextItem();

但这种情况发生得不够频繁,有时我会得到 Uncaught RangeError: Maximum call stack size exceeded,因为递归调用: processNextItem() -> resultHandler() -> processNextItem() -> resultHandler() -> processNextItem() -> resultHandler() -> etc.

为了打破它,我尝试使用setTimeout

this.processNextItem = function () {
    // some work
    ...

    // propagate result
    setTimeout(function () { resultHandler(someResult); }, 0);
}

但这会大大降低性能(我有数百个项目要处理)。

问题

有没有办法通过异步调用中断调用堆栈,但比setTimeout 更有效?或者我应该手动计算同步调用并每 1000 次调用一次 setTimeout 吗?


我的解决方案:解决方法

最初我解决了这个问题,@nepeo 写道:创建了一个同步回调计数器,并使每 1000 个回调异步。这显然是一种解决方法,因为调用堆栈限制因浏览器而异(可能因平台和版本而异)。

最终我通过批处理结果解决了这个问题:

function Worker(resultHandler) {
    this.processNextBatch = function () {
        // some ASYNC work
        ...

        // propagate results
        resultsHandler(someResults);
    }
}   

var myResultsHandler = function (results) {
    results.forEach(function (result) {
        // Process result
        ...
    });

    // Process next item
    worker.processNextBatch();
};

var worker = new Worker(myResultsHandler);

// Start working on first item
Worker.processNextBatch();

但这并没有回复这个帖子,我仍然想知道答案。

【问题讨论】:

    标签: javascript callstack


    【解决方案1】:

    我假设此数据的处理取决于订单?如果不是,我会尝试对调用处理程序的进程函数进行非递归调用,它会更快,并且不会出现堆栈问题!失败的批处理系统可能是最糟糕的解决方法。

    function _batch(callback){
        this.counter = 0;
        this.callback = callback;
        this.count = function(){
            if(this.counter >= 1000){
                setTimeout(callback,0);
                this.counter = 0;
                return false;
            }
            else
                this.counter++
            return true;
        }
    }
    

    如果结果处理程序应该递归,则返回 true。

    还是潜在的重组?如果没有更多信息,很难知道这是否可行。

    var position = 0;
    function process()
    {
        while(1)
        {
            if(!bDataReady){
            setTimeout(process,5);
            break;}
            //process data
            //propagate data
            position ++;
            if(bComplete)
                break
        }
    }
    

    【讨论】:

    • 数据确实取决于顺序。那个批处理系统正是我现在解决它的方法。谢谢,但这看起来更像是解决方法。
    • 听起来很痛苦,这确实是一种解决方法,但我认为这可能是最快的方法,除非您可以将序列重组为非顺序依赖。我想出了一些想法,比如使用 setInterval 创建一个准堆栈线程,但我想它会有同样的性能问题。或者,您可以根据所需的延迟通过服务器处理数据?
    • 我不确定,在这种情况下,非有序数据有什么帮助。你能解释一下吗?我更感兴趣的是一般应该如何解决此类问题,因为它们在回调基础语言中应该很常见,而不是解决我当前的案例(因为它已经在工作了)。
    • 在无序的情况下,您可以在基本循环中启动每个进程调用,然后如果一个进程需要某条数据,您可以使用 setTimeout() 递归该调用,直到数据加载.显然,如果下一个项目依赖于前一个项目的完成,那么进行这样的异步调用可能会在某种程度上破坏它。至于其他语言,JS由于缺乏多线程和睡眠而有些受阻,所以我们不得不使用像setTimeout()这样的混乱回调函数。
    猜你喜欢
    • 2015-03-21
    • 1970-01-01
    • 2016-10-06
    • 1970-01-01
    • 2020-03-13
    • 2011-04-22
    • 1970-01-01
    • 2011-08-07
    • 1970-01-01
    相关资源
    最近更新 更多