Javascript 卡住了setTimeout()s(Chrome)

Javascript 卡住了setTimeout()s(Chrome),javascript,google-chrome,dom-events,settimeout,freeze,Javascript,Google Chrome,Dom Events,Settimeout,Freeze,我遇到过这样的情况:多个setTimeout()调用(典型长度:100ms到1秒)处于活动状态,应该已经关闭,但它们不会启动。Chrome(Mac)调试器配置文件在此(可能无限)期间显示“空闲”。档案显示什么都没有发生。没有循环。没有代码运行。没有垃圾收集。没有任何活动。视口处于活动状态并具有焦点。在我等待(可能是无限长的时间)之后,当我“做其他事情”时,比如mouseover一些不相关的元素——就像:hover一样简单——日志阻塞会中断,排队的setTimeout()s会全部触发 当我在set

我遇到过这样的情况:多个
setTimeout()
调用(典型长度:100ms到1秒)处于活动状态,应该已经关闭,但它们不会启动。Chrome(Mac)调试器配置文件在此(可能无限)期间显示“空闲”。档案显示什么都没有发生。没有循环。没有代码运行。没有垃圾收集。没有任何活动。视口处于活动状态并具有焦点。在我等待(可能是无限长的时间)之后,当我“做其他事情”时,比如
mouseover
一些不相关的元素——就像:hover一样简单——日志阻塞会中断,排队的
setTimeout()
s会全部触发

当我在
setTimeout()
处理程序函数中设置断点时,在发生“冻结”之后,当日志阻塞中断时,它们会按顺序被命中,正如您所期望的那样

不幸的是,复制路径很困难。到目前为止,创建更简单的测试用例只会使复制变得更加困难,或者最终不可能

关于
setTimeout()
“问题”的大部分讨论都来自不了解jscript等单线程特性的人,因此没有什么帮助。让我重复一遍:计时器已排队,应该已启动。浏览器处于空闲状态,探查器证明了这一点。计时器最终会触发,但只有在鼠标活动发生后才会触发。我觉得这种行为很不对。如果浏览器处于空闲状态,并且队列中存在事件,则应触发这些事件

有人见过这样的行为吗?我是否偶然发现了一种锁定事件调度器的方法?也许我错过了什么

更新:无法在Windows 7上复制


更新2:重新启动Mac上的Chrome,无法再复制。所以,最糟糕的结果是:没有答案说明为什么会发生,为什么会持续发生,为什么不会可靠地发生,为什么会消失,为什么不会再发生。

我最近遇到了一个类似的问题,发现自47版以来,chromium用户决定不尊重setTimeout,因为他们认为这“对大多数用户有害”。基本上,他们已经弃用了setTimeout API(通常不询问任何人)。
这里是人们发现这一点的地方。关于这个问题,还有许多其他的bug和讨论线程

回退方法是使用模拟setTimeout。
以下是概念证明:

“严格使用”
var PosfScheduler=(函数(){
/*
*构造器。
*启动计时器收集并启动轮询器。
*/
函数PosfScheduler(){
this.timers={};
这个计数器=0;
this.poll=函数(){
var调度器=这个;
var timers=scheduler.timers;
var now=Date.now();
for(计时器中的var timerId){
var timer=定时器[timerId];
如果(现在-timer.submitDate>=timer.delay){
if(timer.permanent==真){
timer.submitDate=现在;
}否则{
删除计时器[timer.id];
}
timer.func.apply.bind(timer.func、timer.funcobj、timer.funcargs.apply();
}
}
requestAnimationFrame(scheduler.poll.bind(scheduler));
};
这个。poll();
};
/*
*添加计时器。
*计时器可以是
*-间隔(arg[0]:true)-重复超时
*-简单超时(arg[0]:false)
*/
PosfScheduler.prototype.addTimer=函数(){
var id=this.counter++;
永久变量=参数[0];
var func=参数[1];
变量延迟=参数[2];
var funcobj=参数[3];
var funcargs=Array.prototype.slice.call(arguments).slice(4);
var submitDate=Date.now();
变量计时器={
id:id,
永久:永久,
func:func,
延迟:延迟,
funcargs:funcargs,
提交日期:提交日期,
}
this.timers[id]=计时器;
返回计时器;
};
/*
*替换setTimeout
*类似签名:
*setTimeout(函数,延迟[obj,arg1…])
*/
PosfScheduler.prototype.setTimeout=函数(){
var args=Array.prototype.slice.call(参数);
返回this.addTimer.apply.bind(this.addTimer,this[false].concat(args)).apply();
};
/*
*替换setInterval-目前未测试。
*签名:
*设置间隔(函数,延迟[obj,arg1…])
*/
PosfScheduler.prototype.setInterval=函数(){
var args=Array.prototype.slice.call(参数);
返回this.addTimer.apply.bind(this.addTimer,this[true].concat(args)).apply();
};
PosfScheduler.prototype.cancelTimeout=函数(计时器){
删除此.timers[timer.id];
};
/*
*我不想让所有这些调度程序占用javascript线程。
*/
PosfScheduler.prototype.shutdown=函数(){
删除此项;
};
返回PosfScheduler;
})();
var scheduler=new PosfScheduler();
var initTime=Date.now();
var timer1=scheduler.setTimeout(函数(init){
console.log('executing function1(应该出现在'+String(Date.now()-init)+'ms!'之后);
},200,null,initTime);
var timer2=scheduler.setTimeout(函数(init){
console.log('executing function2 after:'+String(Date.now()-init)+'ms!');
},300,null,initTime);
var timer3=scheduler.setTimeout(函数(init){
console.log('在'+String(Date.now()-init)+'ms!'之后执行函数3(不应出现));
},1000,null,initTime);
var timer4=scheduler.setTimeout(函数(init、sched、timer){
console.log