卡住了setTimeout()s(Chrome)

时间:2016-01-30 14:11:12

标签: google-chrome javascript-events settimeout freeze

我遇到多个setTimeout()调用(典型长度:100ms到1秒)处于活动状态且应该已关闭的情况,但它们不会触发。 Chrome(Mac)调试程序配置文件显示"空闲"在这个(可能是无限的)时期。该配置文件显示没有任何进展。没有循环。没有代码在运行。没有垃圾收集。没有任何活动。视口处于活动状态并具有焦点。在我等待(可能是无限时间)之后,当我做其他事情时,像鼠标悬停一些不相关的元素 - 就像a:hover一样简单 - logjam中断,排队的setTimeout()全部触发。

当我在setTimeout()处理函数中设置断点之后"冻结"发生这种情况时,就像你期望的那样,当logjam中断时,它们会按顺序被击中。

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

大多数围绕setTimeout()"问题"来自那些不了解jscript等单线程特性的人,所以没有用。让我再说一遍:计时器排队等候应该解雇。浏览器处于空闲状态,如分析器所证实。定时器最终会触发,但只有在鼠标活动发生后才会触发。这种行为对我来说似乎非常错误。如果浏览器处于空闲状态,并且队列中有事件,则应触发它们。

有没有人见过这样的行为?我偶然发现了锁定事件调度员的方法吗?也许我错过了一些东西 - 谢谢。

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

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

1 个答案:

答案 0 :(得分:0)

我最近遇到了一个类似的问题,发现自47版以来,当他们认为这对大多数用户不利时,铬人决定不尊重setTimeout。基本上他们已经弃用了setTimeout API(像往常一样不问任何人) 以下是人们bug 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 );