Javascript: Pause setTimeout ();

119

Wenn ich eine aktive Zeitüberschreitung habe, die eingestellt wurde var t = setTimeout("dosomething()", 5000),

Gibt es überhaupt eine Pause und Fortsetzung?


Gibt es eine Möglichkeit, die verbleibende Zeit für das aktuelle Zeitlimit abzurufen?
oder muss ich in einer Variablen, wenn das Timeout eingestellt ist, die aktuelle Zeit speichern, dann machen wir eine Pause, bekommen den Unterschied zwischen jetzt und dann?

Hagelholz
quelle
1
Für diejenigen, die sich fragen: Die Pause ist z. B.: Ein Div wird so eingestellt, dass es in 5 Sekunden verschwindet. Nach 3 Sekunden (also noch 2 Sekunden) mausert der Benutzer über das Div. Sie pausieren das Timeout, sobald der Benutzer das Div mausiert Sie setzen es fort, 2 Sekunden später verschwindet es.
Hailwood

Antworten:

260

Sie könnten so einpacken window.setTimeout, was meiner Meinung nach dem ähnlich ist, was Sie in der Frage vorgeschlagen haben:

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();
Tim Down
quelle
3
@yckart: Rollback, sorry. Es ist eine gute Ergänzung, außer dass das Hinzufügen zusätzlicher Parameter setTimeout()in Internet Explorer <= 9 nicht funktioniert.
Tim Down
4
Wenn Sie dies tun, werden timer.resume(); timer.resume();Sie zwei Timeouts parallel haben. Aus diesem Grund möchten Sie entweder clearTimeout(timerId)zuerst oder zu if (timerId) return;Beginn des Lebenslaufs einen Kurzschluss machen.
Kernel
2
Hey, diese Antwort hat mir gefallen, aber ich musste ziehen: var timerId, start, remaining;außerhalb des Klassenbereichs und remaining = delay;wieder innen hinzufügen , um den Parameter zu erfassen. Funktioniert wie ein Zauber tho!
Phillihp
1
@ Josh979: Du musst das wirklich nicht tun und es ist eine schlechte Idee, es zu tun, weil es Variablen verfügbar macht, die intern sein sollten. Vielleicht haben Sie den Code in einen Block eingefügt (z. B. in einen if (blah) { ... }) oder so?
Tim Down
1
Aus irgendeinem Grund läuft dies nach der Initialisierung nur einmal für mich.
Peterxz
17

So etwas sollte den Trick machen.

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 };
}
Sean Vieira
quelle
Ich wechselte +new Date()zu, new Date().getTime()da es schneller ist: jsperf.com/date-vs-gettime
yckart
9

Nein. Sie müssen es abbrechen ( clearTimeout), die Zeit seit dem Start messen und mit der neuen Zeit neu starten.

RoToRa
quelle
7

Eine leicht modifizierte Version der Antwort von Tim Downs . Da Tim jedoch meine Bearbeitung zurückgesetzt hat, muss ich dies selbst beantworten. Meine Lösung ermöglicht es, extra argumentsals dritten (3, 4, 5 ...) Parameter zu verwenden und den Timer zu löschen:

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();
}

Wie Tim erwähnt hat, sind zusätzliche Parameter in nicht verfügbar IE lt 9, aber ich habe ein bisschen herumgearbeitet, damit es auch in funktioniert oldIE.

Verwendung: 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;
yckart
quelle
6

"Pause" und "Lebenslauf" machen im Kontext von nicht viel Sinn setTimeout, was eine einmalige Sache ist. Meinst du setInterval? Wenn ja, nein, Sie können es nicht anhalten, sondern nur abbrechen ( clearInterval) und dann erneut planen. Details von all dies im Timer Abschnitt der Spezifikation.

// 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);
TJ Crowder
quelle
16
Im Kontext von setTimeout sind Pause und Wiederaufnahme immer noch sinnvoll.
dbkaplun
6

Das Timeout war leicht genug, um eine Lösung zu finden, aber das Intervall war etwas kniffliger.

Ich habe mir die folgenden zwei Klassen ausgedacht, um dieses Problem zu lösen:

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

Diese können als solche implementiert werden:

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

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

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

In diesem Beispiel wird ein pausierbares Timeout (pt_hey) festgelegt, das nach zwei Sekunden "hey" alarmieren soll. Ein weiteres Timeout pausiert pt_hey nach einer Sekunde. Ein drittes Timeout setzt pt_hey nach zwei Sekunden fort. pt_hey wird eine Sekunde lang ausgeführt, eine Sekunde lang angehalten und dann fortgesetzt. pt_hey wird nach drei Sekunden ausgelöst.

Nun zu den schwierigeren Intervallen

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

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

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

In diesem Beispiel wird ein pausierbares Intervall (pi_hey) festgelegt, um alle zwei Sekunden "Hallo Welt" in die Konsole zu schreiben. Eine Zeitüberschreitung pausiert pi_hey nach fünf Sekunden. Ein weiteres Timeout setzt pi_hey nach sechs Sekunden fort. Pi_hey wird also zweimal ausgelöst, eine Sekunde lang ausgeführt, eine Sekunde lang angehalten, eine Sekunde lang ausgeführt und dann alle 2 Sekunden weiter ausgelöst.

ANDERE FUNKTIONEN

  • clearTimeout () und clearInterval ()

    pt_hey.clearTimeout();und pi_hey.clearInterval();dienen als einfache Möglichkeit, Zeitüberschreitungen und Intervalle zu löschen.

  • getTimeLeft ()

    pt_hey.getTimeLeft();und pi_hey.getTimeLeft();gibt zurück, wie viele Millisekunden bis der nächste Trigger geplant ist.

TheCrzyMan
quelle
Können Sie Ihre Gedanken erklären, warum wir eine komplexe Klasse brauchen, um eine Pause einzulegen setInterval? Ich denke, ein einfacher if(!true) return;wird den Trick machen, oder irre ich mich?
Yckart
2
Ich habe es so gemacht, dass Sie das Intervall buchstäblich anhalten können, anstatt nur einen Anruf zu überspringen, wenn er ausgelöst wird. Wenn in einem Spiel alle 60 Sekunden ein Power-Up ausgelöst wird und ich das Spiel mit Ihrer Methode kurz vor dem Auslösen pausiere, muss ich eine weitere Minute auf ein weiteres Power-Up warten. Das ist keine wirkliche Pause, das heißt nur, einen Anruf zu ignorieren. Stattdessen wird meine Methode tatsächlich angehalten, und daher wird das Einschalten in Bezug auf das Spiel "pünktlich" freigegeben.
TheCrzyMan
2

Ich musste die verstrichene und verbleibende Zeit berechnen, um einen Fortschrittsbalken anzuzeigen. Es war nicht einfach, die akzeptierte Antwort zu verwenden. 'setInterval' ist für diese Aufgabe besser als 'setTimeout'. Also habe ich diese Timer-Klasse erstellt, die Sie in jedem Projekt verwenden können.

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();
    });
Ashraf Fayad
quelle
2

/beleben

ES6-Version mit syntaktischem Zucker der Klasse y 💋

(leicht modifiziert: start () hinzugefügt)

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>

jeremiah.trein
quelle
1

Sie könnten in clearTimeout () schauen

oder Pause in Abhängigkeit von einer globalen Variablen, die festgelegt wird, wenn eine bestimmte Bedingung erfüllt ist. Wie ein Knopf gedrückt wird.

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

  <script>
  var myBool = false;

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

Sie können es auch mit Ereignissen implementieren.

Anstatt die Zeitdifferenz zu berechnen, starten und beenden Sie das Abhören eines Tick-Ereignisses, das im Hintergrund ausgeführt wird:

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

};
meleyal
quelle
1

Wenn Sie ohnehin jquery verwenden, lesen Sie das Plugin $ .doTimeout . Diese Sache ist eine enorme Verbesserung gegenüber setTimeout, einschließlich der Möglichkeit, Ihre Zeitüberschreitungen mit einer einzelnen Zeichenfolgen-ID zu verfolgen, die Sie angeben und die sich nicht jedes Mal ändert, wenn Sie sie festlegen, und einfaches Abbrechen, Abrufen von Schleifen und Entprellen zu implementieren Mehr. Eines meiner am häufigsten verwendeten JQuery-Plugins.

Leider wird Pause / Resume nicht sofort unterstützt. Dazu müssten Sie $ .doTimeout umbrechen oder erweitern, vermutlich ähnlich wie bei der akzeptierten Antwort.

Ben Roberts
quelle
Ich hatte gehofft, dass doTimeout eine Pause / Wiederaufnahme haben würde, aber ich sehe es nicht, wenn ich mir die vollständige Dokumentation, Schleifenbeispiele und sogar die Quelle ansehe. Die nächste Pause, die ich sehen konnte, ist Abbrechen, aber dann müsste ich den Timer mit Funktion erneut erstellen. Habe ich etwas verpasst?
Ericlaw
Tut mir leid, Sie auf den falschen Weg geführt zu haben. Ich habe diese Ungenauigkeit aus meiner Antwort entfernt.
Ben Roberts
1

Ich musste in der Lage sein, setTimeout () für eine Diashow-ähnliche Funktion anzuhalten.

Hier ist meine eigene Implementierung eines pausierbaren Timers. Es integriert Kommentare zu Tim Downs Antwort, wie z. B. eine bessere Pause (Kernel-Kommentar) und eine Form des Prototyping (Umur Gediks Kommentar).

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 */

Beispiel:

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

Um den Code zu testen, verwenden Sie timer.resume()und timer.pause()einige Male und überprüfen Sie, wie viel Zeit noch übrig ist. (Stellen Sie sicher, dass Ihre Konsole geöffnet ist.)

Die Verwendung dieses Objekts anstelle von setTimeout () ist so einfach wie das Ersetzen timerID = setTimeout( mycallback, 1000)durch timer = new Timer( mycallback, 1000 ). Dann timer.pause()und timer.resume()stehen Ihnen zur Verfügung.

Fabien Snauwaert
quelle
1
function delay (ms)   {  return new Promise(resolve => setTimeout(resolve, s));  }

"asynchrone" Arbeitsdemo unter: site zarsoft.info

Joe Oliver
quelle
0

Typoskript-Implementierung basierend auf der am besten bewerteten Antwort

/** 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;
        }
    }

}
cuddlemeister
quelle
0

Sie können wie folgt vorgehen, um setTimeout auf der Serverseite pausierbar zu machen (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();
};

und Sie können es wie unten überprüfen

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)
Manoj Rana
quelle
-1

Ich glaube nicht, dass Sie etwas Besseres als clearTimeout finden werden . Auf jeden Fall können Sie später jederzeit ein anderes Timeout einplanen, anstatt es wieder aufzunehmen.

Nikita Rybak
quelle
-1

Wenn Sie mehrere Divs ausblenden müssen, können Sie einen setIntervalund mehrere Zyklen verwenden, um Folgendes zu tun:

<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>
Mic
quelle