2016-01-30 2 views
1

У меня есть ситуация, когда несколько вызовов setTimeout() (типичная длина: от 100 мс до 1 с) активны и должны были уйти, но они не срабатывают. Профиль отладки Chrome (Mac) показывает «простоя» во время этого (потенциально бесконечного) периода. Профиль показывает, что ничего не происходит. Нет циклов. Не работает код. Никакой сборки мусора. Никакой активности вообще. Видовой экран активен и имеет фокус. После того, как я буду ждать (возможно, бесконечное время), когда я «делаю что-то еще», например, наведение на какой-то неродственный элемент - так же просто, как: hover - ломающиеся перерывы и очередь в очереди setTimeout().Застрял setTimeout() s (Chrome)

Когда я устанавливаю точки останова в функциях обработчика setTimeout() после этого «замораживания», они попадают в последовательность, когда ломаная ломается, как и следовало ожидать.

К сожалению, путь репликации затруднен. Пока что создание более простых тестовых примеров просто делает репликацию еще более сложной или, в конечном счете, невозможной.

Большая часть болтовни вокруг setTimeout() «проблемы» - это люди, которые не понимают однопоточность jscript и т. Д., Поэтому это не помогает. Позвольте мне повторить: таймеры поставлены в очередь и должны были выстрелить. Браузер простаивает, как доказал профайлер. Таймеры ДОЛЖНЫ стрелять в конечном счете, но только после активности мыши. Такое поведение кажется мне очень неправильным. Если браузер неактивен, и в очереди есть события, они должны срабатывать.

Кто-нибудь видел такое поведение? Я наткнулся на способ блокировки диспетчера событий? Может, мне что-то не хватает - спасибо.

Обновление: Не удается воспроизвести на Windows 7.

Update 2: перезапущен Chrome на Mac, больше не может повторить. Итак, худший возможный результат: нет ответа, почему это произошло, почему это продолжалось, почему это не произошло надежно, почему оно ушло, и почему этого больше не будет.

ответ

0

Недавно у меня была аналогичная проблема, и я обнаружил, что начиная с версии 47 хромные люди решили не соблюдать setTimeout, когда считают, что это «наносит ущерб большинству пользователей». В основном они устарели API setTimeout (никого не спрашивая, как обычно).
Здесь bug 570845, где люди об этом узнали. Существует ряд других ошибок и обсуждений по этой проблеме.

Резервная копия предназначена для эмуляции setTimeout с использованием requestAnimationFrame.
Вот доказательство концепции:

'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);

Смежные вопросы