Javascript 在web worker中包装setTimeout()时递归过多

Javascript 在web worker中包装setTimeout()时递归过多,javascript,web-worker,Javascript,Web Worker,我试图将self.setTimeout()函数包装在web worker中(因此没有本机窗口对象)。我认为使用方法apply语法会非常简单: window.setTimeout = function ( /**/) { return setTimeout.apply(self,arguments); }; 此调用应相当于使用任意数量的参数(函数、延迟、传递给函数的参数)调用self.setTimeout(),并返回超时的id 然而,中间的代码行最终抛出“太多递归”错误。这样调用它似乎

我试图将self.setTimeout()函数包装在web worker中(因此没有本机窗口对象)。我认为使用方法apply语法会非常简单:

window.setTimeout = function ( /**/) {
    return setTimeout.apply(self,arguments); 
};
此调用应相当于使用任意数量的参数(函数、延迟、传递给函数的参数)调用self.setTimeout(),并返回超时的id

然而,中间的代码行最终抛出“太多递归”错误。这样调用它似乎打破了这里描述的机制:它破坏了以前的函数上下文,使它实际上是递归的。以防万一,它是特定于浏览器的:在Firefox 74.0(64位)中测试

一些背景

以防万一,如果有人想知道,我为什么要这样做:

我想在不重写所有内容的情况下,将一些CPU密集型代码从主线程转移到web工作者

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

作为解决方法,我在工人内部使用。因为我实际上不想做DOM操作,所以我只是以最简单的方式添加模拟DOM中缺少的任何特性。但出于某种原因,库在某个时候显式地调用window.setTimeout(),而不仅仅是setTimeout()——因此我需要将此函数添加到模拟窗口对象中,它需要正常工作

更新

感谢Alexandre Senges指出此函数实际上将调用自身的错误

web Worker内部实际工作的解决方案是

window.setTimeout = function ( /**/) {
    return DedicatedWorkerGlobalScope.prototype.setTimeout.apply(self,arguments); 
};
因此,我需要编写函数的整个路径,以防止window.setTimeout调用自身

更新2


事实上,正如Kaido的另一个答案所指出的,我最初的想法本身应该很好地发挥作用。发生过多递归的原因是,我在代码的另一部分中犯了一个错误,该部分有效地复制了
self.setTimeout=window.setTimeout
——从而导致
setTimeout===self.setTimeout==window.setTimeout
,因此函数window.setTimeout突然间是无意的递归。

当您多次调用同一函数本身时,就会产生深度。原因是每次调用函数时,都必须为其所有堆栈变量创建新实例

例如:

const recursive = (x) => {
    console.log(x);
    recursive(x+1);
}
在这里,每次函数调用自身时,它都会创建一个新的x,并将其推送到堆栈中,而不会弹出上一个x,因为我们仍然没有从调用方返回。如果我们让这种情况发生的时间太长,堆栈将被填满,我们将得到一个
StackOverflow
(因此是网站的名称)

现在,JS通过限制调用堆栈的深度来防止这种情况发生

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

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

然而,我认为你试图做的一切都是一个坏主意。您不应该尝试替换
窗口
方法,因为它会为您的所有程序甚至可能依赖它们的库更改它。如果我是你,我只需创建一个新函数
myTimeout

你的代码应该可以正常工作:

const worker\u脚本=`
常量窗口={};
window.setTimeout=函数(/**/){
返回setTimeout.apply(self,arguments);
};
setTimeout((val)=>postMessage('done'+val),200,'ok');
`;
const blob=新blob([worker_script]);
const url=url.createObjectURL(blob);
const worker=新的worker(url);
worker.onmessage=e=>console.log(e.data);

worker.onerror=console.error在创建window.setTimeout之前不存在它。正如我在第一句话中所说的,我在一个网络工作者内部,而网络工作者没有窗口对象。所有全局变量都是专用WorkerGlobalScope类型对象的一部分。因此,如果我只写setTimeout(),这实际上是来自专用WorkerGlobalScope的setTimeout,而不是窗口。setTimeout()@wulf21 I不会改变它调用自身的事实。您必须更改为window.setTimeout=()=>IdeficatedWorkerGlobalScope.setTimeout()…@wulf21我不熟悉WebWorkers,但问题是相同的,您在其内部调用相同的函数,感谢您的提示。这足以找到解决办法。我会将这个答案标记为正确,并更新我的帖子,说明我是如何解决这个问题的。是的,这个答案实际上更有意义。我无法理解为什么我的原始代码会导致一个无休止的递归循环,只是说这可能是由于JS的解释性本质,并接受了它。但要澄清的是:我没有将
window
设置为
self
,但该窗口实际上是一个模拟dom窗口:
var window=$dom({docReadyState:“complete”})但代码中可能还有其他部分(我自己的或库)出于某种原因设置self.setTimeout=window.setTimeout。您的答案确实完全正确(考虑到我的代码的其他部分可能会做什么)。我犯了一个小错误,但没有抛出语法错误。此错误导致代码位将所有成员从
窗口
有效复制到
self
——包括
设置超时
——而不是将库X注入
窗口
的每个新成员复制到self,目的是什么,