您好,我在上面的问题有点含糊,因此我将尝试使其更加清晰。
我有以下形式的代码:
function main () {
async function recursive () {
var a = "Hello World";
var b = "Goodbye World";
recursive();
}
recursive();
}
我遇到堆内存不足的问题。
假设上面显示的是程序的行为方式,并且在递归函数中声明了a和b,我的问题是变量是否在递归函数中调用递归时被销毁,或者它们是否会持续存在假设我保持主函数运行足够长的时间,以免发生递归调用,并且主函数到达其端点。
我担心它们仍然存在于堆中,并且由于我的真实程序在这些变量中存储了大字符串,因此我担心这就是我耗尽堆的原因。
答案 0 :(得分:7)
JavaScript还没有(还?)具有用于递归的尾调用优化,因此,可以肯定的是,您最终将填满您的调用堆栈。堆被填满的原因是因为recursive
被定义为async
函数,并且从不等待分配的Promise
对象解析。这将填满您的堆,因为垃圾收集器没有机会收集它们。
简而言之,除非您要在函数中的堆中分配内容,否则递归不会耗尽堆。
扩展正在发生的事情:
main
new Promise(() => {
new Promise(() => {
new Promise(() => {
new Promise(() => {
new Promise(() => { //etc.
您说您不是在等待Promises,因为您希望事物并行运行。由于某些原因,这是一种危险的方法。万一抛出承诺,应该始终等待它们,当然,您要确保分配的内存使用量是可预测的。如果此递归函数意味着您获得了无法预测的所需任务数量,则应考虑任务队列模式:
const tasks = [];
async function recursive() {
// ...
tasks.push(workItem);
// ...
}
async function processTasks() {
while (tasks.length) {
workItem = tasks.unshift();
// Process work item. May provoke calls to `recursive`
// ...do awaits here for the async parts
}
}
async function processWork(maxParallelism) {
const workers = [];
for (let i = 0; i < maxParallelism; i++) {
workers.push(processTasks());
}
await Promise.all(workers);
}
最后,如果需要说一句,async
函数不允许您进行并行处理。它们只是提供了一种更方便地编写Promises并进行顺序和/或并行等待异步事件的方式。除非您使用Web Worker之类的语言,否则JavaScript仍然是单线程执行引擎。因此,使用异步函数尝试并行化同步任务对您无济于事。