在网络工作者中包装setTimeout()时递归过多

问题描述 投票:0回答:1

我正在尝试将self.setTimeout()函数包装在网络工作者中(因此没有本机窗口对象)。我认为使用方法apply语法会很简单:

window.setTimeout = function ( /**/) {
    return setTimeout.apply(self,arguments); 
};

此调用应等效于使用任意数量的参数(函数,延迟,传递给函数的参数)并返回超时ID来调用self.setTimeout()。

但是,代码的中间行最终会引发“太多的递归”错误。看起来像这样调用它某种程度上破坏了这里描述的机制:Will a recursive 'setTimeout' function call eventually kill the JS Engine?破坏了先前的函数上下文并使之实际上是递归的。以防万一它是特定于浏览器的:在Firefox 74.0(64位)中经过测试。

某些背景

以防万一有人在想,为什么我要尝试这个:

我想将一些占用大量CPU的代码从主线程转移到Web工作者,而无需重写所有内容。

不幸的是,代码依赖于某些库,而这些库又依赖于存在的窗口和文档,否则它将不会初始化。

作为一种解决方法,我正在工作人员内部使用Mock Dom。因为我实际上不想执行DOM操作,所以我只是以最简单的方式添加了模拟dom中缺少的所有功能。但是由于某种原因,该库有时会显式调用window.setTimeout()而不是setTimeout()-因此,我需要将此函数添加到模拟窗口对象中,并且它需要正常工作。

javascript web-worker
1个回答
0
投票

最大递归深度发生在您一次又一次地重复调用同一函数太多次时。原因是每次调用该函数时,都必须为其所有堆栈变量创建新实例。

例如:

const recursive = (x) => {
    console.log(x);
    recursive(x+1);
}

这里,每次函数调用自身时,它都会创建一个新的x并将其推入堆栈,而不会弹出先前的堆栈,因为我们仍未从调用方返回。如果我们让这种情况发生太长时间,则堆栈将满,并且我们会得到StackOverflow(因此是网站的名称。)

现在,JS通过限制调用堆栈的深度来保护我们避免发生这种情况。

在您的示例中,问题是由于您无意中创建了一个递归函数。当您调用新的window.setTimeout时,它将调用setTimeout.apply,它指向新函数本身,因此它将再次调用自身,等等。一种解决方法是将前一个方法提取到新变量中,然后再调用它: >

window.oldTimeout = window.setTimeout;
window.setTimeout = function ( /**/) {
    return oldTimeout.apply(self,arguments); 
};

但是我认为您要尝试做的只是一个坏主意。您不应该尝试替换window方法,因为它会为您的所有程序甚至是依赖于它们的库更改它。如果我是您,我将简单创建一个新函数myTimeout

© www.soinside.com 2019 - 2024. All rights reserved.