简体   繁体   English

卡住 setTimeout()s (Chrome)

[英]Stuck setTimeout()s (Chrome)

I have a situation where multiple setTimeout() calls (typical length: 100ms to 1 sec) are active and should have gone off, but they do not fire.我遇到了多个setTimeout()调用(典型长度:100 毫秒到 1 秒)处于活动状态并且应该关闭的情况,但它们没有触发。 A Chrome (Mac) debugger profile shows "idle" during this (potentially infinite) period. Chrome (Mac) 调试器配置文件在此(可能无限)期间显示“空闲”。 The profile shows that nothing is going on.配置文件显示没有任何事情发生。 No loops.没有循环。 No code running.没有代码运行。 No garbage collection.没有垃圾收集。 No activity whatsoever.没有任何活动。 The viewport is active and has focus.视口处于活动状态并具有焦点。 After I wait (potentially an infinite time), when I "do something else", like mouseover some unrelated element -- as simple as a :hover -- the logjam breaks and the queued setTimeout() s all fire.在我等待(可能是无限的时间)之后,当我“做其他事情”时,比如将mouseover一些不相​​关的元素上——就像 :hover 一样简单——僵局打破了,排队的setTimeout()都被触发了。

When I set breakpoints in the setTimeout() handler functions after this "freeze" occurs, they get hit in sequence when the logjam breaks, just as you would expect.当我在“冻结”发生后在setTimeout()处理函数中设置断点时,它们会在卡顿中断时按顺序命中,正如您所期望的那样。

Unfortunately the replication path is difficult.不幸的是,复制路径很困难。 So far, creating simpler test cases just makes replication even more difficult or, ultimately, impossible.到目前为止,创建更简单的测试用例只会让复制变得更加困难,或者最终是不可能的。

Most of the chatter around setTimeout() "issues" is from people who don't understand the single-thread nature of jscript, etc., so it is not helpful.大多数围绕setTimeout() “问题”的喋喋不休来自不了解 jscript 等单线程性质的人,所以它没有帮助。 Let me repeat: the timers are queued and should have fired.让我再说一遍:计时器已排队,应该已触发。 The browser is idle, as proved by the profiler.正如分析器所证明的那样,浏览器处于空闲状态。 The timers DO fire ultimately, but only after mouse activity occurs.计时器最终会触发,但仅在鼠标活动发生后触发。 This behavior seems very wrong to me.这种行为对我来说似乎非常错误。 If the browser is idle, and there are events in the queue, they should fire.如果浏览器空闲,并且队列中有事件,它们应该被触发。

Has anyone seen behavior like this?有没有人见过这样的行为? Have I stumbled across a way to lock up the event dispatcher?我是否偶然发现了一种锁定事件调度程序的方法? Maybe I'm missing something.也许我错过了一些东西。

Update: Cannot replicate on Windows 7.更新:无法在 Windows 7 上复制。

Update 2: Restarted Chrome on Mac, can no longer replicate.更新 2:在 Mac 上重新启动 Chrome,无法再复制。 So, worst possible outcome: no answer as to why it happened, why it kept happening, why it didn't happen reliably, why it went away, and why it won't happen any more.所以,最糟糕的可能结果:没有回答为什么会发生,为什么会一直发生,为什么它没有可靠地发生,为什么它消失了,为什么它不会再发生了。

I had a similar issue recently and discovered that since version 47, chromium folks have decided to not honour setTimeout when they believe this is 'detrimental to the majority of users'.我最近遇到了类似的问题,并发现自 47 版以来,铬人决定不尊重 setTimeout,因为他们认为这“对大多数用户有害”。 Basically they've deprecated the setTimeout API (asking no one, as usual).基本上他们已经弃用了 setTimeout API(像往常一样不问任何人)。
Here is bug 570845 where people discovered about this.这是人们发现的错误 570845 There are a number of other bugs and discussion threads regarding the issue.关于该问题,还有许多其他错误和讨论主题。

The fallback is to emulate setTimeout using requestAnimationFrame .回退是使用requestAnimationFrame模拟 setTimeout 。
Here is a proof of concept:这是一个概念证明:

 'use strict' var PosfScheduler = ( function () { /* * Constructor. * Initiate the collection of timers and start the poller. */ function PosfScheduler () { this.timers = {}; this.counter = 0; this.poll = function () { var scheduler = this; var timers = scheduler.timers; var now = Date.now(); for ( var timerId in timers ) { var timer = timers[timerId]; if ( now - timer.submitDate >= timer.delay ) { if ( timer.permanent === true ) { timer.submitDate = now; } else { delete timers[timer.id]; } timer.func.apply.bind( timer.func, timer.funcobj, timer.funcargs ).apply(); } } requestAnimationFrame( scheduler.poll.bind(scheduler) ); }; this.poll(); }; /* * Adding a timer. * A timer can be * - an interval (arg[0]: true) - a recurrent timeout * - a simple timeout (arg[0]: false) */ PosfScheduler.prototype.addTimer = function () { var id = this.counter++; var permanent = arguments[0] ; var func = arguments[1] ; var delay = arguments[2] ; var funcobj = arguments[3] ; var funcargs = Array.prototype.slice.call(arguments).slice(4); var submitDate = Date.now() ; var timer = { id: id, permanent: permanent, func: func, delay: delay, funcargs: funcargs, submitDate: submitDate, } this.timers[id] = timer; return timer; }; /* * Replacement for setTimeout * Similar signature: * setTimeout ( function, delay [obj,arg1...] ) */ PosfScheduler.prototype.setTimeout = function () { var args = Array.prototype.slice.call(arguments); return this.addTimer.apply.bind( this.addTimer, this, [false].concat(args) ).apply(); }; /* * Replacement for setInterval - Untested for now. * Signature: * setInterval ( function, delay [obj,arg1...] ) */ PosfScheduler.prototype.setInterval = function () { var args = Array.prototype.slice.call(arguments); return this.addTimer.apply.bind( this.addTimer, this, [true].concat(args) ).apply(); }; PosfScheduler.prototype.cancelTimeout = function ( timer ) { delete this.timers[timer.id]; }; /* * Don't want to leave all these schedulers hogging the javascript thread. */ PosfScheduler.prototype.shutdown = function () { delete this; }; return PosfScheduler; })(); var scheduler = new PosfScheduler(); var initTime = Date.now(); var timer1 = scheduler.setTimeout ( function ( init ) { console.log ('executing function1 (should appear) after ' + String ( Date.now() - init ) + 'ms!' ); }, 200, null, initTime ); var timer2 = scheduler.setTimeout ( function ( init ) { console.log ('executing function2 afte: ' + String ( Date.now() - init ) + 'ms!' ); }, 300, null, initTime ); var timer3 = scheduler.setTimeout ( function ( init ) { console.log ('executing function3 (should not appear) after ' + String ( Date.now() - init ) + 'ms!' ); }, 1000, null, initTime ); var timer4 = scheduler.setTimeout ( function ( init, sched, timer ) { console.log ('cancelling timer3 after ' + String ( Date.now() - init ) + 'ms!' ); sched.cancelTimeout ( timer3 ); }, 500, null, initTime, scheduler, timer3 ); var timer5 = scheduler.setInterval ( function ( init, sched, timer ) { console.log ('periodic after ' + String ( Date.now() - init ) + 'ms!' ); }, 400, null, initTime, scheduler, timer3 ); var timer6 = scheduler.setTimeout ( function ( init, sched, timer ) { console.log ('cancelling periodic after ' + String ( Date.now() - init ) + 'ms!' ); sched.cancelTimeout ( timer5 ); }, 900, null, initTime, scheduler, timer5 );

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM