从回调函数中递归调用一个函数会导致堆栈溢出吗?

7

我希望在事件触发后调用一个函数,然后在同一回调中再次调用该函数。这是为了创建一个类似于事件监听器的效果,当函数完成时。

当您看到代码时,您将知道我想要做什么:

"use strict";
var page = require('webpage').create();
var system = require('system');

function onStdReadLine(callback) {
    system.stdin.readLineAsync(function(err, line) {
        callback(line);
        onStdReadLine(callback);
    });
}

onStdReadLine(function(line) {
    // do something when the line comes in
    system.stdout.writeLine(line);
}); 

问题:

这段代码会不会产生栈溢出?有没有一种方法可以重构这段代码,使其不再递归?

谢谢!


如果是异步的,它就不在同一个“线程”(或轮次)上,因此不应该溢出堆栈。我想知道是否存在与调试和Chrome开发工具选项显示异步堆栈跟踪相关的问题。话虽如此,我曾经在Firefox中看到过8年前由于递归链接setTimeout调用而导致的堆栈溢出。 - Ruan Mendes
2个回答

6

这会导致栈溢出吗?

不会引起栈溢出。异步回调在堆栈完全展开时被调用,因此没有堆栈积累。

记住,异步操作是如何工作的。JavaScript 的当前线程启动了异步操作。然后,该异步操作由其他本机代码管理。JavaScript 的当前线程然后运行到其完成并结束(从而清除了堆栈)。

稍后,正在运行异步操作的本机代码看到操作已完成,并向 JavaScript 事件队列发布一个事件,以调用该异步操作的回调。当 JS 引擎没有运行其他任何内容时,它将从队列中取出该事件并处理它(调用回调)。在调用回调时,将有一个空的堆栈帧。

回调函数会有一个活动作用域对象,但是在 JavaScript 中,作用域与堆栈帧完全分离。

唯一可能有问题的情况是,如果您的回调被同步调用。那么,显然会有堆栈积累和可能发生堆栈溢出的风险。但只要回调始终是异步调用的(应该是这样的),就没有问题。

有没有一种重构这段代码使其不递归的方法?

在 JavaScript 异步编程中,从上一个迭代的完成运行下一个迭代,并从其中调用一个函数是非常有用的设计模式。没有理由重构它。


2
不会导致堆栈溢出。当您调用异步函数时,原始函数返回,因此其堆栈帧被释放。回调函数是从事件处理程序而不是原始函数中调用的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接