繁体   English   中英

卡住 setTimeout()s (Chrome)

[英]Stuck setTimeout()s (Chrome)

我遇到了多个setTimeout()调用(典型长度:100 毫秒到 1 秒)处于活动状态并且应该关闭的情况,但它们没有触发。 Chrome (Mac) 调试器配置文件在此(可能无限)期间显示“空闲”。 配置文件显示没有任何事情发生。 没有循环。 没有代码运行。 没有垃圾收集。 没有任何活动。 视口处于活动状态并具有焦点。 在我等待(可能是无限的时间)之后,当我“做其他事情”时,比如将mouseover一些不相​​关的元素上——就像 :hover 一样简单——僵局打破了,排队的setTimeout()都被触发了。

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

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

大多数围绕setTimeout() “问题”的喋喋不休来自不了解 jscript 等单线程性质的人,所以它没有帮助。 让我再说一遍:计时器已排队,应该已触发。 正如分析器所证明的那样,浏览器处于空闲状态。 计时器最终会触发,但仅在鼠标活动发生后触发。 这种行为对我来说似乎非常错误。 如果浏览器空闲,并且队列中有事件,它们应该被触发。

有没有人见过这样的行为? 我是否偶然发现了一种锁定事件调度程序的方法? 也许我错过了一些东西。

更新:无法在 Windows 7 上复制。

更新 2:在 Mac 上重新启动 Chrome,无法再复制。 所以,最糟糕的可能结果:没有回答为什么会发生,为什么会一直发生,为什么它没有可靠地发生,为什么它消失了,为什么它不会再发生了。

我最近遇到了类似的问题,并发现自 47 版以来,铬人决定不尊重 setTimeout,因为他们认为这“对大多数用户有害”。 基本上他们已经弃用了 setTimeout API(像往常一样不问任何人)。
这是人们发现的错误 570845 关于该问题,还有许多其他错误和讨论主题。

回退是使用requestAnimationFrame模拟 setTimeout 。
这是一个概念证明:

 '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