javascript: setTimeout'u duraklat ();


118

Ayarlanmış etkin bir zaman aşımım varsa var t = setTimeout("dosomething()", 5000),

Duraklatmanın ve devam ettirmenin bir yolu var mı?


Geçerli zaman aşımında kalan süreyi almanın bir yolu var mı?
yoksa bir değişkende mi olmalıyım, zaman aşımı ayarlandığında, şimdiki zamanı kaydettikten sonra dururuz, şimdi ile o zaman arasındaki farkı anlarız?


1
Merak edenler için, Duraklatma örneğin: a div 5 saniye içinde kaybolacak şekilde ayarlanır, 3 saniyede (yani 2 saniye kaldı), kullanıcı fareyi div üzerine getirir, kullanıcı fareyi div 'i kapattıktan sonra zaman aşımını duraklatırsınız. devam ettirirseniz, 2 saniye sonra kaybolur.
Hailwood

Yanıtlar:


261

window.setTimeoutŞöyle toparlayabilirsin , ki bence soruda önerdiğin şeye benzer:

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: Üzgünüm geri alındı. setTimeout()Internet Explorer'a ek parametreler eklemenin işe yaramaması dışında iyi bir ektir <= 9.
Tim Down

4
Bunu yaparsanız timer.resume(); timer.resume();, paralel olarak iki zaman aşımına sahip olursunuz. Bu yüzden ya clearTimeout(timerId)ilk önce ya da if (timerId) return;özgeçmişin başında kısa devre yapmak istersiniz .
kernel

2
Hey, bu cevabı beğendim, ama şunu çıkarmalıydım: var timerId, start, remaining;Sınıf kapsamının dışına remaining = delay;ve parametreyi yakalamak için tekrar içeri eklemem gerekiyor. Bir cazibe gibi çalışıyor tho!
phillihp

1
@ Josh979: Bunu gerçekten yapmanıza gerek yok ve bunu yapmak kötü bir fikir çünkü dahili olması gereken değişkenleri ortaya çıkarıyor. Belki kodu bir bloğun (örneğin bir bloğun içine if (blah) { ... }) veya başka bir şeyin içine yapıştırmışsınızdır ?
Tim Down

1
Bazı nedenlerden dolayı bu, başlatıldıktan sonra benim için yalnızca bir kez çalışır.
peterxz

17

Bunun gibi bir şey hile yapmalı.

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

Daha hızlı olduğu +new Date()için değiştirdim new Date().getTime(): jsperf.com/date-vs-gettime
yckart

9

Hayır. Cancel it ( clearTimeout) yapmanız, başlattığınız andan itibaren geçen zamanı ölçmeniz ve yeni saatle yeniden başlatmanız gerekecek .


7

Tim Downs cevabının biraz değiştirilmiş bir versiyonu . Ancak, Tim düzenlememi geri aldığından, bunu kendim yanıtlamam gerekiyor. Benim çözümüm, argumentsüçüncü (3, 4, 5 ...) parametre olarak ekstra kullanmayı ve zamanlayıcıyı temizlemeyi mümkün kılıyor :

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

Tim'in bahsettiği gibi, ekstra parametreler uygulamada mevcut değil IE lt 9, ancak ben de çalışabilmesi için biraz oldIEuğraştım.

Kullanımı: 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

"Duraklat" ve "devam ettirmek" bağlamında pek bir anlam ifade etmiyor setTimeout, ki bu tek seferlik bir şey. Şunu musunuz setInterval? Öyleyse, hayır, duraklatamazsınız, yalnızca iptal edebilir ( clearInterval) ve sonra yeniden programlayabilirsiniz. Tüm bunların ayrıntıları , spesifikasyonun Zamanlayıcılar bölümünde .

// 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 bağlamında, duraklatma ve devam ettirme hala mantıklıdır.
dbkaplun

6

Zaman Aşımı bir çözüm bulmak için yeterince kolaydı, ancak Aralık biraz daha yanıltıcıydı.

Bu sorunları çözmek için aşağıdaki iki sınıfı buldum:

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

Bunlar şu şekilde uygulanabilir:

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

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

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

Bu örnek, iki saniye sonra "hey" uyarısı vermek üzere planlanan duraklatılabilir bir Zaman Aşımı (pt_hey) ayarlayacaktır. Başka bir Zaman Aşımı, bir saniye sonra pt_hey'i duraklatır. Üçüncü bir Zaman Aşımı, iki saniye sonra pt_hey'e devam eder. pt_hey bir saniye çalışır, bir saniye duraklatır, ardından çalışmaya devam eder. pt_hey, üç saniye sonra tetiklenir.

Şimdi daha zorlu aralıklar için

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

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

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

Bu örnek, konsolda her iki saniyede bir "merhaba dünya" yazmak için duraklatılabilir bir Aralık (pi_hey) ayarlar. Bir zaman aşımı, beş saniye sonra pi_hey'i duraklatır. Başka bir zaman aşımı, altı saniye sonra pi_hey'e devam eder. Böylece pi_hey iki kez tetiklenecek, bir saniye çalışacak, bir saniye duraklayacak, bir saniye çalışacak ve ardından her 2 saniyede bir tetiklemeye devam edecektir.

DİĞER FONKSİYONLAR

  • clearTimeout () ve clearInterval ()

    pt_hey.clearTimeout();ve pi_hey.clearInterval();zaman aşımlarını ve aralıkları temizlemenin kolay bir yolu olarak hizmet eder.

  • getTimeLeft ()

    pt_hey.getTimeLeft();ve pi_hey.getTimeLeft();sonraki tetikleyicinin gerçekleşmesi planlanan kaç milisaniye kadar dönecektir.


Karmaşık bir neden ihtiyaç düşüncelerinizi açıklayabilir, Class bir duraklatmak için setInterval? Bence basit bir if(!true) return;hile yapacak mı yoksa yanılıyor muyum?
yckart

2
Tetiklendiğinde bir aramayı atlamak yerine, aralığı kelimenin tam anlamıyla duraklatabilmeniz için yaptım. Bir oyunda, her 60 saniyede bir güç açılırsa ve oyunu tetiklenmeden hemen önce sizin yönteminizi kullanarak duraklatırsam, başka bir açılış için bir dakika daha beklemem gerekecek. Bu gerçekten duraklamak değil, sadece bir aramayı görmezden gelmektir. Bunun yerine, Benim yöntemim aslında duraklıyor ve bu nedenle, oyun oynamaya göre güç verme 'zamanında' yayınlanıyor.
TheCrzyMan

2

Bir ilerleme çubuğunu göstermek için geçen ve kalan süreyi hesaplamam gerekiyordu. Kabul edilen cevabı kullanmak kolay olmadı. Bu görev için 'setInterval', 'setTimeout'tan daha iyidir. Bu yüzden, herhangi bir projede kullanabileceğiniz bu Timer sınıfını yarattım.

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

/canlandırmak

Sınıf-y sözdizimsel şeker kullanan ES6 Sürümü 💋

(biraz değiştirildi: başlangıç ​​eklendi ())

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 () 'a bakabilirsiniz

veya belirli bir koşula ulaşıldığında ayarlanan genel bir değişkene bağlı olarak duraklama. Bir düğmeye basıldığı gibi.

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

  <script>
  var myBool = false;

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

1

Bunu olaylarla da uygulayabilirsiniz.

Zaman farkını hesaplamak yerine, arka planda çalışmaya devam eden bir 'tik' olayını dinlemeye başlar ve durdurursunuz:

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

Yine de jquery kullanıyorsanız, $ .doTimeout eklentisine bakın. Bu şey, setTimeout'a göre çok büyük bir gelişmedir; belirlediğiniz ve her ayarladığınızda değişmeyen tek bir dize kimliği ile zaman aşımlarınızı takip etmenize ve kolay iptal etme, yoklama döngüleri ve hata bildirme uygulamanıza izin verir. Daha. En çok kullanılan jquery eklentilerimden biri.

Ne yazık ki, kutudan çıktığı anda duraklatmayı / devam ettirmeyi desteklemiyor. Bunun için, muhtemelen kabul edilen yanıta benzer şekilde $ .doTimeout'u kaydırmanız veya uzatmanız gerekir.


DoTimeout'un duraklatılacağını / devam ettirileceğini umuyordum, ancak tam belgelere, döngü örneklerine ve hatta kaynağa baktığımda görmüyorum. Görebildiğim en yakın duraklamaya iptal, ancak daha sonra zamanlayıcıyı işlevle yeniden oluşturmam gerekir. Bir şey mi kaçırdım?
ericslaw

Sizi yanlış yola yönlendirdiğim için üzgünüm. Bu yanlışlığı cevabımdan kaldırdım.
Ben Roberts

1

Slayt gösterisi benzeri özellik için setTimeout () işlevini duraklatabilmem gerekiyordu.

Duraklatılabilir bir zamanlayıcı için kendi uygulamam burada. Tim Down'ın cevabında görülen, daha iyi duraklama (çekirdek yorumu) ve bir tür prototip oluşturma (Umur Gedik'in yorumu) gibi yorumları bütünleştirir.

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

Misal:

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

Kodu test etmek için timer.resume()ve timer.pause()birkaç kez kullanın ve ne kadar zaman kaldığını kontrol edin. (Konsolunuzun açık olduğundan emin olun.)

Bu nesneyi setTimeout () yerine kullanmak, timerID = setTimeout( mycallback, 1000)ile değiştirmek kadar kolaydır timer = new Timer( mycallback, 1000 ). O zaman timer.pause()ve timer.resume()sizin için uygun.



0

En yüksek puan alan yanıta dayalı tip komut uygulaması

/** 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'u sunucu tarafında (Node.js) duraklatmak için aşağıdaki gibi yapabilirsiniz.

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

ve aşağıdaki gibi kontrol edebilirsiniz

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'tan daha iyi bir şey bulacağınızı sanmıyorum . Her neyse, onu 'devam ettirmek' yerine daha sonra her zaman başka bir zaman aşımı planlayabilirsiniz.


-1

Gizlenecek birkaç div'iniz varsa, aşağıdaki setIntervalgibi yapmak için bir ve birkaç döngü kullanabilirsiniz:

<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>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.