javascript: tạm dừng setTimeout ();


119

Nếu tôi có thời gian chờ đang hoạt động đã được thiết lập thông qua var t = setTimeout("dosomething()", 5000),

Có cách nào để tạm dừng và tiếp tục không?


Có cách nào để lấy thời gian còn lại trên timeout hiện tại không?
hay tôi có phải trong một biến, khi thời gian chờ được đặt, lưu trữ thời gian hiện tại, sau đó chúng ta tạm dừng, lấy chênh lệch giữa bây giờ và sau đó?


1
Đối với những người đang thắc mắc, Việc tạm dừng là ví dụ: một div được thiết lập để biến mất sau 5 giây, ở 3 giây (vì vậy còn 2 giây) người dùng di chuột qua div, bạn tạm dừng thời gian chờ, sau khi người dùng rời khỏi div bạn tiếp tục nó, 2 giây sau nó biến mất.
Hailwood

Câu trả lời:


260

Bạn có thể kết thúc window.setTimeoutnhư thế này, mà tôi nghĩ tương tự như những gì bạn đã đề xuất trong câu hỏi:

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: Đã cuộn lại, xin lỗi. Đó là một bổ sung tốt ngoại trừ việc thêm tham số bổ sung để setTimeout()không làm việc trong Internet Explorer <= 9.
Tim Xuống

4
Nếu bạn làm vậy, timer.resume(); timer.resume();bạn sẽ có hai thời gian chờ song song. Đó là lý do tại sao bạn muốn làm clearTimeout(timerId)trước hoặc làm if (timerId) return;ngắn mạch ngay khi bắt đầu sơ yếu lý lịch.
kernel

2
Này, tôi thích câu trả lời này, nhưng tôi phải kéo: var timerId, start, remaining;bên ngoài phạm vi Lớp và thêm remaining = delay;lại bên trong để bắt tham số. Hoạt động như một cái duyên tho!
phillihp

1
@ Josh979: Bạn thực sự không cần phải làm điều đó và đó là một ý tưởng tồi khi làm điều đó vì nó làm lộ các biến đáng lẽ phải ở bên trong. Có thể bạn đã dán mã bên trong một khối (ví dụ bên trong một if (blah) { ... }) hoặc một cái gì đó?
Tim Down

1
Vì một số lý do, điều này chỉ chạy một lần đối với tôi sau khi được khởi tạo.
peterxz,

17

Một cái gì đó như thế này nên làm thủ thuật.

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

Tôi đã thay đổi +new Date()đến new Date().getTime()vì nó là nhanh hơn: jsperf.com/date-vs-gettime
yckart

9

Không. Bạn cần phải hủy nó ( clearTimeout), đo thời gian kể từ khi bạn khởi động nó và khởi động lại nó với thời gian mới.


7

Một phiên bản sửa đổi một chút của câu trả lời của Tim Downs . Tuy nhiên, kể từ khi Tim quay lại chỉnh sửa của tôi, tôi phải tự trả lời điều này. Giải pháp của tôi giúp bạn có thể sử dụng thêm thông số argumentsthứ ba (3, 4, 5 ...) và xóa bộ đếm thời gian:

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

Như Tim đã đề cập, các thông số bổ sung không có sẵn IE lt 9, tuy nhiên tôi đã làm việc một chút để nó cũng hoạt động oldIE.

Sử dụng: 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

"Tạm dừng" và "tiếp tục" không thực sự có nhiều ý nghĩa trong bối cảnh setTimeout, đó là điều chỉ xảy ra . Ý bạn là setInterval? Nếu vậy, không, bạn không thể tạm dừng nó, bạn chỉ có thể hủy nó ( clearInterval) rồi lên lịch lại. Chi tiết về tất cả những điều này trong phần Bộ hẹn giờ của thông số kỹ thuật.

// 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
Trong bối cảnh setTimeout, tạm dừng và tiếp tục vẫn có ý nghĩa.
dbkaplun

6

Thời gian chờ đủ dễ dàng để tìm ra giải pháp, nhưng Khoảng thời gian thì phức tạp hơn một chút.

Tôi đã nghĩ ra hai lớp sau để giải quyết vấn đề này:

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

Những điều này có thể được thực hiện như sau:

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

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

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

Ví dụ này sẽ đặt Thời gian chờ có thể tạm dừng (pt_hey) được lên lịch để cảnh báo, "hey" sau hai giây. Thời gian chờ khác tạm dừng pt_hey sau một giây. Thời gian chờ thứ ba tiếp tục pt_hey sau hai giây. pt_hey chạy trong một giây, tạm dừng trong một giây, sau đó tiếp tục chạy. pt_hey sẽ kích hoạt sau ba giây.

Bây giờ cho những khoảng thời gian phức tạp hơn

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

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

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

Ví dụ này đặt Khoảng thời gian có thể tạm dừng (pi_hey) để viết "hello world" trong bảng điều khiển cứ hai giây một lần. Hết thời gian tạm dừng pi_hey sau năm giây. Một thời gian chờ khác sẽ tiếp tục pi_hey sau sáu giây. Vì vậy, pi_hey sẽ kích hoạt hai lần, chạy trong một giây, tạm dừng trong một giây, chạy trong một giây và sau đó tiếp tục kích hoạt sau mỗi 2 giây.

CAC CHƯC NĂNG KHAC

  • clearTimeout ()clearInterval ()

    pt_hey.clearTimeout();pi_hey.clearInterval();phục vụ như một cách dễ dàng để xóa thời gian chờ và khoảng thời gian.

  • getTimeLeft ()

    pt_hey.getTimeLeft();pi_hey.getTimeLeft();sẽ trả về bao nhiêu mili giây cho đến khi kích hoạt tiếp theo được lên lịch để xảy ra.


Bạn có thể giải thích suy nghĩ của mình, tại sao chúng ta cần một Class phức tạp để tạm dừng a setInterval? Tôi nghĩ rằng một ý muốn đơn giản if(!true) return;sẽ làm được thủ thuật, hay tôi đã sai?
yckart,

2
Tôi đã tạo ra nó để bạn có thể tạm dừng khoảng thời gian theo đúng nghĩa đen, thay vì chỉ bỏ qua một cuộc gọi khi nó kích hoạt. Nếu, trong một trò chơi, một lần tăng sức mạnh được phát hành sau mỗi 60 giây và tôi tạm dừng trò chơi ngay trước khi trò chơi sắp kích hoạt, bằng cách sử dụng phương pháp của bạn, tôi sẽ phải đợi thêm một phút cho một lần tăng sức mạnh khác. Điều đó không thực sự tạm dừng, đó chỉ là bỏ qua một cuộc gọi. Thay vào đó, phương pháp của tôi thực sự đang tạm dừng, và do đó, sức mạnh tăng lên được phát hành 'đúng lúc' đối với trò chơi-play.
TheCrzyMan

2

Tôi cần tính toán thời gian đã trôi qua và thời gian còn lại để hiển thị thanh tiến trình. Thật không dễ dàng khi sử dụng câu trả lời được chấp nhận. 'setInterval' tốt hơn 'setTimeout' cho tác vụ này. Vì vậy, tôi đã tạo lớp Timer này mà bạn có thể sử dụng trong bất kỳ dự án nào.

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

/ hồi sinh

Phiên bản ES6 sử dụng cú pháp Class-y 💋

(sửa đổi một chút: thêm start ())

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

Bạn có thể nhìn vào clearTimeout ()

hoặc tạm dừng tùy thuộc vào biến toàn cục được đặt khi một điều kiện nhất định được thực hiện. Giống như một nút được nhấn.

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

  <script>
  var myBool = false;

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

1

Bạn cũng có thể thực hiện nó với các sự kiện.

Thay vì tính toán chênh lệch thời gian, bạn bắt đầu và dừng nghe một sự kiện 'đánh dấu' tiếp tục chạy trong nền:

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

Nếu bạn đang sử dụng jquery, hãy xem $ .doTimeout plugin . Đây là một cải tiến lớn so với setTimeout, bao gồm việc cho phép bạn theo dõi thời gian chờ của mình với một id chuỗi duy nhất mà bạn chỉ định và không thay đổi mỗi khi bạn đặt nó, đồng thời triển khai dễ dàng hủy bỏ, vòng bỏ phiếu và gỡ lỗi, và hơn. Một trong những plugin jquery được sử dụng nhiều nhất của tôi.

Thật không may, nó không hỗ trợ tạm dừng / tiếp tục ra khỏi hộp. Đối với điều này, bạn sẽ cần phải bọc hoặc mở rộng $ .doTimeout, có lẽ tương tự như câu trả lời được chấp nhận.


Tôi đã hy vọng doTimeout sẽ tạm dừng / tiếp tục, nhưng tôi không thấy điều đó khi xem toàn bộ tài liệu, ví dụ về vòng lặp và thậm chí cả nguồn. Lần tạm dừng gần nhất mà tôi có thể thấy là hủy bỏ, nhưng sau đó tôi sẽ phải tạo lại bộ hẹn giờ có chức năng một lần nữa. Tôi đã bỏ lỡ điều gì đó?
ericslaw

Xin lỗi vì đã dẫn bạn đi sai đường. Tôi đã loại bỏ sự không chính xác đó khỏi câu trả lời của mình.
Ben Roberts

1

Tôi cần có thể tạm dừng setTimeout () cho tính năng giống như trình chiếu.

Đây là cách triển khai bộ hẹn giờ có thể tạm dừng của riêng tôi. Nó tích hợp các nhận xét về câu trả lời của Tim Down, chẳng hạn như tạm dừng tốt hơn (nhận xét của hạt nhân) và một hình thức tạo mẫu (nhận xét của Umur Gedik.)

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

Thí dụ:

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

Để kiểm tra mã, hãy sử dụng timer.resume()timer.pause()một vài lần và kiểm tra xem còn lại bao nhiêu thời gian. (Đảm bảo rằng bảng điều khiển của bạn đang mở.)

Sử dụng đối tượng này thay cho setTimeout () dễ dàng như thay thế timerID = setTimeout( mycallback, 1000)bằng timer = new Timer( mycallback, 1000 ). Sau đó timer.pause()timer.resume()có sẵn cho bạn.



0

Triển khai bảng chữ dựa trên câu trả lời được xếp hạng cao nhất

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

Bạn có thể làm như dưới đây để làm cho setTimeout có thể tạm dừng ở phía máy chủ (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();
};

và bạn có thể kiểm tra nó như bên dưới

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

Tôi không nghĩ rằng bạn sẽ tìm thấy bất kỳ thứ gì tốt hơn clearTimeout . Dù sao, bạn luôn có thể lên lịch thời gian chờ khác sau đó, thay vì 'tiếp tục' nó.


-1

Nếu bạn có một số div cần ẩn, bạn có thể sử dụng một setIntervalvà một số chu kỳ để thực hiện như trong:

<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>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.