javascript: пауза setTimeout ();


119

Якщо у мене активний час очікування, який було встановлено var t = setTimeout("dosomething()", 5000),

Чи варто все-таки призупинити та відновити його?


Чи є спосіб отримати час, що залишився на поточному тайм-ауті?
або я повинен у змінній, коли встановлено тайм-аут, зберігати поточний час, тоді ми робимо паузу, отримуємо різницю між часом і часом?


1
Для тих, хто цікавиться, Пауза призначена для: наприклад, div встановлюється, щоб зникнути через 5 секунд, через 3 секунди (так що залишилося 2 секунди) користувач переводить миші над дівом, ви призупиняєте тайм-аут, як тільки користувач вимкне дів ви відновите його, через 2 секунди він зникає.
Гейлвуд

Відповіді:


260

Ви можете обернути window.setTimeoutце так, що, на мою думку, схоже на те, що ви пропонували у питанні:

var Timer = function(callback, delay) {
    var timerId, start, remaining = delay;

    this.pause = function() {
        window.clearTimeout(timerId);
        remaining -= Date.now() - start;
    };

    this.resume = function() {
        start = Date.now();
        window.clearTimeout(timerId);
        timerId = window.setTimeout(callback, remaining);
    };

    this.resume();
};

var timer = new Timer(function() {
    alert("Done!");
}, 1000);

timer.pause();
// Do some stuff...
timer.resume();

3
@yckart: Відкотився, вибач. Це гарне доповнення, окрім того, що додавання додаткових параметрів setTimeout()не працює в Internet Explorer <= 9.
Tim Down

4
Якщо ви зробите це, timer.resume(); timer.resume();ви отримаєте паралельно два тайм-аути. Ось чому ви хочете або clearTimeout(timerId)спочатку або зробити if (timerId) return;коротке замикання на самому початку резюме.
ядро

2
Гей, мені ця відповідь сподобалася, але мені довелося витягнути: var timerId, start, remaining;поза рамками класу та додати remaining = delay;всередину, щоб зловити параметр. Працює як шарм Тхо!
phillihp

1
@ Josh979: Вам дійсно не потрібно цього робити, і це погана ідея, оскільки це відкриває змінні, які повинні бути внутрішніми. Можливо, ви вставили код всередину блоку (наприклад, всередині if (blah) { ... }) або щось таке?
Тим Даун

1
Чомусь це для мене працює лише один раз після ініціалізації.
peterxz

17

Щось подібне повинно зробити трюк.

function Timer(fn, countdown) {
    var ident, complete = false;

    function _time_diff(date1, date2) {
        return date2 ? date2 - date1 : new Date().getTime() - date1;
    }

    function cancel() {
        clearTimeout(ident);
    }

    function pause() {
        clearTimeout(ident);
        total_time_run = _time_diff(start_time);
        complete = total_time_run >= countdown;
    }

    function resume() {
        ident = complete ? -1 : setTimeout(fn, countdown - total_time_run);
    }

    var start_time = new Date().getTime();
    ident = setTimeout(fn, countdown);

    return { cancel: cancel, pause: pause, resume: resume };
}

Я змінив +new Date()до , new Date().getTime()так як це швидше: jsperf.com/date-vs-gettime
yckart

9

Ні. Вам потрібно буде скасувати його ( clearTimeout), виміряти час, коли ви його запустили, і перезавантажте його з новим часом.


7

Трохи модифікована версія Тіма Даунс відповідає . Однак, оскільки Тім відмовив свою редакцію, я сам повинен відповісти на це. Моє рішення дозволяє використовувати додатковий argumentsяк третій (3, 4, 5 ...) параметр і очистити таймер:

function Timer(callback, delay) {
    var args = arguments,
        self = this,
        timer, start;

    this.clear = function () {
        clearTimeout(timer);
    };

    this.pause = function () {
        this.clear();
        delay -= new Date() - start;
    };

    this.resume = function () {
        start = new Date();
        timer = setTimeout(function () {
            callback.apply(self, Array.prototype.slice.call(args, 2, args.length));
        }, delay);
    };

    this.resume();
}

Як згадував Тім, додаткові параметри недоступні IE lt 9, проте я трохи попрацював, щоб він працювавoldIE .

Використання: new Timer(Function, Number, arg1, arg2, arg3...)

function callback(foo, bar) {
    console.log(foo); // "foo"
    console.log(bar); // "bar"
}

var timer = new Timer(callback, 1000, "foo", "bar");

timer.pause();
document.onclick = timer.resume;

6

"Пауза" та "відновлення" насправді не мають великого сенсу в контексті setTimeout, що є разовою справою. Ви маєте на увазі setInterval? Якщо це так, ні, ви не можете призупинити його, ви можете лише скасувати його ( clearInterval) та повторно запланувати його ще раз. Детально про все це в розділі Таймери специфікації.

// Setting
var t = setInterval(doSomething, 1000);

// Pausing (which is really stopping)
clearInterval(t);
t = 0;

// Resuming (which is really just setting again)
t = setInterval(doSomething, 1000);

16
У контексті setTimeout пауза та відновлення все ще мають сенс.
dbkaplun

6

Час очікування був досить легким, щоб знайти рішення, але інтервал був дещо складнішим.

Я вирішив наступні два класи, щоб вирішити цю проблему:

function PauseableTimeout(func, delay){
    this.func = func;

    var _now = new Date().getTime();
    this.triggerTime = _now + delay;

    this.t = window.setTimeout(this.func,delay);

    this.paused_timeLeft = 0;

    this.getTimeLeft = function(){
        var now = new Date();

        return this.triggerTime - now;
    }

    this.pause = function(){
        this.paused_timeLeft = this.getTimeLeft();

        window.clearTimeout(this.t);
        this.t = null;
    }

    this.resume = function(){
        if (this.t == null){
            this.t = window.setTimeout(this.func, this.paused_timeLeft);
        }
    }

    this.clearTimeout = function(){ window.clearTimeout(this.t);}
}

function PauseableInterval(func, delay){
    this.func = func;
    this.delay = delay;

    this.triggerSetAt = new Date().getTime();
    this.triggerTime = this.triggerSetAt + this.delay;

    this.i = window.setInterval(this.func, this.delay);

    this.t_restart = null;

    this.paused_timeLeft = 0;

    this.getTimeLeft = function(){
        var now = new Date();
        return this.delay - ((now - this.triggerSetAt) % this.delay);
    }

    this.pause = function(){
        this.paused_timeLeft = this.getTimeLeft();
        window.clearInterval(this.i);
        this.i = null;
    }

    this.restart = function(sender){
        sender.i = window.setInterval(sender.func, sender.delay);
    }

    this.resume = function(){
        if (this.i == null){
            this.i = window.setTimeout(this.restart, this.paused_timeLeft, this);
        }
    }

    this.clearInterval = function(){ window.clearInterval(this.i);}
}

Вони можуть бути реалізовані як такі:

var pt_hey = new PauseableTimeout(function(){
    alert("hello");
}, 2000);

window.setTimeout(function(){
    pt_hey.pause();
}, 1000);

window.setTimeout("pt_hey.start()", 2000);

Цей приклад встановить призупинений час очікування (pt_hey), який планується попередити "ей" через дві секунди. Ще один тайм-аут призупиняє pt_hey через одну секунду. Третій час очікування поновлюється pt_hey через дві секунди. pt_hey працює протягом однієї секунди, робить паузу на одну секунду, після чого продовжує працювати. pt_hey спрацьовує через три секунди.

Тепер про складніші інтервали

var pi_hey = new PauseableInterval(function(){
    console.log("hello world");
}, 2000);

window.setTimeout("pi_hey.pause()", 5000);

window.setTimeout("pi_hey.resume()", 6000);

Цей приклад встановлює паузальний інтервал (pi_hey), щоб писати "привіт світ" кожні дві секунди. Час очікування призупиняє pi_hey через п’ять секунд. Ще один тайм-аут поновлюється pi_hey через шість секунд. Так pi_hey запустить двічі, запустити одну секунду, зробити паузу на одну секунду, запустити одну секунду, а потім продовжувати ініціювати кожні 2 секунди.

ІНШІ ФУНКЦІЇ

  • clearTimeout () та clearInterval ()

    pt_hey.clearTimeout(); і pi_hey.clearInterval(); служать простим способом очищення тайм-аутів та інтервалів.

  • getTimeLeft ()

    pt_hey.getTimeLeft();і pi_hey.getTimeLeft();поверне скільки мілісекунд до наступного тригеру, як заплановано.


Чи можете ви пояснити свої думки, для чого нам потрібен складний Клас, щоб зробити паузу setInterval? Я думаю, що простий if(!true) return;зробить трюк, чи я помиляюся?
yckart

2
Я зробив це так, що ви можете буквально призупинити інтервал, замість того, щоб просто пропустити дзвінок, коли він спрацьовує. Якщо в грі вимкнення живлення випускається кожні 60 секунд, і я призупиняю гру безпосередньо перед тим, як вона вже запуститься, використовуючи ваш метод, мені доведеться почекати ще одну хвилину, щоб черговий пристрій увімкнути. Це не справді пауза, це просто ігнорування дзвінка. Натомість, Мій метод насправді призупиняється, і, отже, живлення виходить "вчасно" стосовно гри.
TheCrzyMan

2

Мені потрібно було обчислити пройдений і залишився час, щоб показати смугу прогресу. Використовувати прийняту відповідь було непросто. 'setInterval' краще для 'setTimeout' для цього завдання. Отже, я створив цей клас Таймера, який можна використовувати в будь-якому проекті.

https://jsfiddle.net/ashraffayad/t0mmv853/

'use strict';


    //Constructor
    var Timer = function(cb, delay) {
      this.cb = cb;
      this.delay = delay;
      this.elapsed = 0;
      this.remaining = this.delay - self.elapsed;
    };

    console.log(Timer);

    Timer.prototype = function() {
      var _start = function(x, y) {
          var self = this;
          if (self.elapsed < self.delay) {
            clearInterval(self.interval);
            self.interval = setInterval(function() {
              self.elapsed += 50;
              self.remaining = self.delay - self.elapsed;
              console.log('elapsed: ' + self.elapsed, 
                          'remaining: ' + self.remaining, 
                          'delay: ' + self.delay);
              if (self.elapsed >= self.delay) {
                clearInterval(self.interval);
                self.cb();
              }
            }, 50);
          }
        },
        _pause = function() {
          var self = this;
          clearInterval(self.interval);
        },
        _restart = function() {
          var self = this;
          self.elapsed = 0;
          console.log(self);
          clearInterval(self.interval);
          self.start();
        };

      //public member definitions
      return {
        start: _start,
        pause: _pause,
        restart: _restart
      };
    }();


    // - - - - - - - - how to use this class

    var restartBtn = document.getElementById('restart');
    var pauseBtn = document.getElementById('pause');
    var startBtn = document.getElementById('start');

    var timer = new Timer(function() {
      console.log('Done!');
    }, 2000);

    restartBtn.addEventListener('click', function(e) {
      timer.restart();
    });
    pauseBtn.addEventListener('click', function(e) {
      timer.pause();
    });
    startBtn.addEventListener('click', function(e) {
      timer.start();
    });

2

/ відроджувати

Версія ES6 з використанням синтаксичного цукру Class-y 💋

(трохи змінено: додано старт ())

class Timer {
  constructor(callback, delay) {
    this.callback = callback
    this.remainingTime = delay
    this.startTime
    this.timerId
  }

  pause() {
    clearTimeout(this.timerId)
    this.remainingTime -= new Date() - this.startTime
  }

  resume() {
    this.startTime = new Date()
    clearTimeout(this.timerId)
    this.timerId = setTimeout(this.callback, this.remainingTime)
  }

  start() {
    this.timerId = setTimeout(this.callback, this.remainingTime)
  }
}

// supporting code
const pauseButton = document.getElementById('timer-pause')
const resumeButton = document.getElementById('timer-resume')
const startButton = document.getElementById('timer-start')

const timer = new Timer(() => {
  console.log('called');
  document.getElementById('change-me').classList.add('wow')
}, 3000)

pauseButton.addEventListener('click', timer.pause.bind(timer))
resumeButton.addEventListener('click', timer.resume.bind(timer))
startButton.addEventListener('click', timer.start.bind(timer))
<!doctype html>
<html>
<head>
  <title>Traditional HTML Document. ZZz...</title>
  <style type="text/css">
    .wow { color: blue; font-family: Tahoma, sans-serif; font-size: 1em; }
  </style>
</head>
<body>
  <h1>DOM &amp; JavaScript</h1>

  <div id="change-me">I'm going to repaint my life, wait and see.</div>

  <button id="timer-start">Start!</button>
  <button id="timer-pause">Pause!</button>
  <button id="timer-resume">Resume!</button>
</body>
</html>


1

Ви можете заглянути в clearTimeout ()

або призупинити залежно від глобальної змінної, яка встановлюється під час досягнення певної умови. Наче натискається кнопка.

  <button onclick="myBool = true" > pauseTimeout </button>

  <script>
  var myBool = false;

  var t = setTimeout(function() {if (!mybool) {dosomething()}}, 5000);
  </script>

1

Ви також можете реалізувати це з подіями.

Замість того, щоб обчислювати різницю в часі, ви починаєте і перестаєте слухати подію "галочки", яка продовжує працювати у фоновому режимі:

var Slideshow = {

  _create: function(){                  
    this.timer = window.setInterval(function(){
      $(window).trigger('timer:tick'); }, 8000);
  },

  play: function(){            
    $(window).bind('timer:tick', function(){
      // stuff
    });       
  },

  pause: function(){        
    $(window).unbind('timer:tick');
  }

};

1

Якщо ви будь-коли використовуєте jquery, перегляньте $ .doTimeout плагін . Ця річ є величезним покращенням порівняно з setTimeout, включаючи те, що ви можете відслідковувати час очікування за допомогою одного ідентифікатора рядка, який ви вказуєте, і який не змінюється щоразу, коли ви його встановите, та впроваджуйте просте скасування, опитування циклів та розмову та більше. Один із моїх найбільш використовуваних плагінів jquery.

На жаль, він не підтримує паузу / відновлення поза межами поля. Для цього вам потрібно буде обернути або розширити $ .doTimeout, імовірно, аналогічно прийнятій відповіді.


Я сподівався, що doTimeout матиме паузу / відновити, але я не бачу цього при перегляді повної документації, циклічних прикладів і навіть джерела. Найближча до паузи, яку я міг бачити, - скасувати, але тоді мені доведеться знову відтворити таймер з функцією. Я щось пропустив?
ericslaw

Вибачте, що ведете вас неправильним шляхом. Я усунув цю неточність зі своєї відповіді.
Бен Робертс

1

Мені потрібно було мати змогу призупинити setTimeout () для функції, подібної до слайд-шоу.

Ось моя власна реалізація таймера, що призупиняється. Він об'єднує коментарі, які бачать відповідь Тіма Дауна, такі як краща пауза (коментар ядра) та форма прототипування (коментар Умура Гедіка.)

function Timer( callback, delay ) {

    /** Get access to this object by value **/
    var self = this;



    /********************* PROPERTIES *********************/
    this.delay = delay;
    this.callback = callback;
    this.starttime;// = ;
    this.timerID = null;


    /********************* METHODS *********************/

    /**
     * Pause
     */
    this.pause = function() {
        /** If the timer has already been paused, return **/
        if ( self.timerID == null ) {
            console.log( 'Timer has been paused already.' );
            return;
        }

        /** Pause the timer **/
        window.clearTimeout( self.timerID );
        self.timerID = null;    // this is how we keep track of the timer having beem cleared

        /** Calculate the new delay for when we'll resume **/
        self.delay = self.starttime + self.delay - new Date().getTime();
        console.log( 'Paused the timer. Time left:', self.delay );
    }


    /**
     * Resume
     */
    this.resume = function() {
        self.starttime = new Date().getTime();
        self.timerID = window.setTimeout( self.callback, self.delay );
        console.log( 'Resuming the timer. Time left:', self.delay );
    }


    /********************* CONSTRUCTOR METHOD *********************/

    /**
     * Private constructor
     * Not a language construct.
     * Mind var to keep the function private and () to execute it right away.
     */
    var __construct = function() {
        self.starttime = new Date().getTime();
        self.timerID = window.setTimeout( self.callback, self.delay )
    }();    /* END __construct */

}   /* END Timer */

Приклад:

var timer = new Timer( function(){ console.log( 'hey! this is a timer!' ); }, 10000 );
timer.pause();

Щоб перевірити код, використовуйте timer.resume()таtimer.pause() кілька разів і перевірте, скільки часу залишилось. (Переконайтеся, що ваша консоль відкрита.)

Використовувати цей об'єкт замість setTimeout () так само просто, як замінити timerID = setTimeout( mycallback, 1000)на timer = new Timer( mycallback, 1000 ). Тоді timer.pause()і вам timer.resume()доступні.



0

Реалізація машинопису на основі відповіді з найкращим рейтингом

/** Represents the `setTimeout` with an ability to perform pause/resume actions */
export class Timer {
    private _start: Date;
    private _remaining: number;
    private _durationTimeoutId?: NodeJS.Timeout;
    private _callback: (...args: any[]) => void;
    private _done = false;
    get done () {
        return this._done;
    }

    constructor(callback: (...args: any[]) => void, ms = 0) {
        this._callback = () => {
            callback();
            this._done = true;
        };
        this._remaining = ms;
        this.resume();
    }

    /** pauses the timer */
    pause(): Timer {
        if (this._durationTimeoutId && !this._done) {
            this._clearTimeoutRef();
            this._remaining -= new Date().getTime() - this._start.getTime();
        }
        return this;
    }

    /** resumes the timer */
    resume(): Timer {
        if (!this._durationTimeoutId && !this._done) {
            this._start = new Date;
            this._durationTimeoutId = setTimeout(this._callback, this._remaining);
        }
        return this;
    }

    /** 
     * clears the timeout and marks it as done. 
     * 
     * After called, the timeout will not resume
     */
    clearTimeout() {
        this._clearTimeoutRef();
        this._done = true;
    }

    private _clearTimeoutRef() {
        if (this._durationTimeoutId) {
            clearTimeout(this._durationTimeoutId);
            this._durationTimeoutId = undefined;
        }
    }

}

0

Ви можете зробити так, як нижче, щоб зробити setTimeout призупиненим на стороні сервера (Node.js)

const PauseableTimeout = function(callback, delay) {
    var timerId, start, remaining = delay;

    this.pause = function() {
        global.clearTimeout(timerId);
        remaining -= Date.now() - start;
    };

    this.resume = function() {
        start = Date.now();
        global.clearTimeout(timerId);
        timerId = global.setTimeout(callback, remaining);
    };

    this.resume();
};

і ви можете перевірити це як нижче

var timer = new PauseableTimeout(function() {
    console.log("Done!");
}, 3000);
setTimeout(()=>{
    timer.pause();
    console.log("setTimeout paused");
},1000);

setTimeout(()=>{
    console.log("setTimeout time complete");
},3000)

setTimeout(()=>{
    timer.resume();
    console.log("setTimeout resume again");
},5000)

-1

Я не думаю, що ти знайдеш щось краще, ніж clearTimeout . У будь-якому випадку, ви завжди можете запланувати інший тайм-аут пізніше, замість цього "відновити" його.


-1

Якщо у вас є кілька дівок, які ви можете приховати, ви можете використовувати setIntervalта кілька циклів, як це зробити:

<div id="div1">1</div><div id="div2">2</div>
<div id="div3">3</div><div id="div4">4</div>
<script>
    function hideDiv(elm){
        var interval,
            unit = 1000,
            cycle = 5,
            hide = function(){
                interval = setInterval(function(){
                    if(--cycle === 0){
                        elm.style.display = 'none';
                        clearInterval(interval);
                    }
                    elm.setAttribute('data-cycle', cycle);
                    elm.innerHTML += '*';
                }, unit);
            };
        elm.onmouseover = function(){
            clearInterval(interval);
        };
        elm.onmouseout = function(){
            hide();
        };
        hide();
    }
    function hideDivs(ids){
        var id;
        while(id = ids.pop()){
            hideDiv(document.getElementById(id));
        }
    }
    hideDivs(['div1','div2','div3','div4']);
</script>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.