tìm thời gian còn lại trong setTimeout ()?


90

Tôi đang viết một số Javascript tương tác với mã thư viện mà tôi không sở hữu và không thể (hợp lý) thay đổi. Nó tạo ra thời gian chờ Javascript được sử dụng để hiển thị câu hỏi tiếp theo trong một loạt các câu hỏi giới hạn thời gian. Đây không phải là mã thực vì nó bị xáo trộn ngoài mọi hy vọng. Đây là những gì thư viện đang làm:

....
// setup a timeout to go to the next question based on user-supplied time
var t = questionTime * 1000
test.currentTimeout = setTimeout( showNextQuestion(questions[i+1]), t );

Tôi muốn đặt một thanh tiến trình trên màn hình lấp đầy questionTime * 1000bằng cách hỏi bộ hẹn giờ được tạo bởi setTimeout. Vấn đề duy nhất là, dường như không có cách nào để làm điều này. Có getTimeoutchức năng nào mà tôi đang thiếu không? Thông tin duy nhất về thời gian chờ của Javascript mà tôi có thể tìm thấy chỉ liên quan đến việc tạo setTimeout( function, time)và xóa thông qua clearTimeout( id ).

Tôi đang tìm một hàm trả về thời gian còn lại trước khi hết thời gian kích hoạt hoặc thời gian trôi qua sau khi hết thời gian được gọi. Mã vạch tiến trình của tôi trông như thế này:

var  timeleft = getTimeout( test.currentTimeout ); // I don't know how to do this
var  $bar = $('.control .bar');
while ( timeleft > 1 ) {
    $bar.width(timeleft / test.defaultQuestionTime * 1000);
}

tl; dr: Làm cách nào để tìm thời gian còn lại trước một javascript setTimeout ()?


Đây là giải pháp tôi đang sử dụng bây giờ. Tôi đã xem qua phần thư viện phụ trách các bài kiểm tra và giải mã (tồi tệ và chống lại quyền của tôi).

// setup a timeout to go to the next question based on user-supplied time
var t = questionTime * 1000
test.currentTimeout = mySetTimeout( showNextQuestion(questions[i+1]), t );

và đây là mã của tôi:

// trình bao bọc cho setTimeout
function mySetTimeout (func, timeout) {
    hết giờ [n = setTimeout (func, timeout)] = {
        start: new Date (). getTime (),
        end: new Date (). getTime () + timeout
        t: thời gian chờ
    }
    trả về n;
}

Điều này hoạt động khá tốt trên bất kỳ trình duyệt nào không phải là IE 6. Ngay cả iPhone ban đầu, nơi tôi mong đợi mọi thứ trở nên không đồng bộ.

Câu trả lời:


31

Nếu bạn không thể sửa đổi mã thư viện, bạn cần phải xác định lại setTimeout cho phù hợp với mục đích của mình. Đây là một ví dụ về những gì bạn có thể làm:

(function () {
var nativeSetTimeout = window.setTimeout;

window.bindTimeout = function (listener, interval) {
    function setTimeout(code, delay) {
        var elapsed = 0,
            h;

        h = window.setInterval(function () {
                elapsed += interval;
                if (elapsed < delay) {
                    listener(delay - elapsed);
                } else {
                    window.clearInterval(h);
                }
            }, interval);
        return nativeSetTimeout(code, delay);
    }

    window.setTimeout = setTimeout;
    setTimeout._native = nativeSetTimeout;
};
}());
window.bindTimeout(function (t) {console.log(t + "ms remaining");}, 100);
window.setTimeout(function () {console.log("All done.");}, 1000);

Đây không phải là mã sản xuất, nhưng nó sẽ đưa bạn đi đúng hướng. Lưu ý rằng bạn chỉ có thể ràng buộc một người nghe mỗi thời gian chờ. Tôi chưa thực hiện thử nghiệm rộng rãi với điều này, nhưng nó hoạt động trong Firebug.

Một giải pháp mạnh mẽ hơn sẽ sử dụng cùng một kỹ thuật gói setTimeout, nhưng thay vào đó, sử dụng một bản đồ từ timeoutId trả về cho các trình nghe để xử lý nhiều trình nghe trên mỗi thời gian chờ. Bạn cũng có thể xem xét gói clearTimeout để bạn có thể tách bộ lắng nghe của mình nếu thời gian chờ bị xóa.


Cảm ơn bạn Bạn đã cứu ngày của tôi!
xecutioner

14
Do thời gian thực thi, điều này có thể trôi đi vài mili giây trong một thời gian. Một cách an toàn hơn để làm điều này, là ghi lại thời gian khi bạn bắt đầu thời gian chờ (Ngày mới ()) và sau đó chỉ cần trừ nó khỏi thời gian hiện tại (Ngày mới khác ())
Jonathan

61

Chỉ đối với bản ghi, có một cách để lấy lại thời gian trong node.js:

var timeout = setTimeout(function() {}, 3600 * 1000);

setInterval(function() {
    console.log('Time left: '+getTimeLeft(timeout)+'s');
}, 2000);

function getTimeLeft(timeout) {
    return Math.ceil((timeout._idleStart + timeout._idleTimeout - Date.now()) / 1000);
}

Bản in:

$ node test.js 
Time left: 3599s
Time left: 3597s
Time left: 3595s
Time left: 3593s

Điều này dường như không hoạt động trong firefox thông qua, nhưng vì node.js là javascript, tôi nghĩ rằng nhận xét này có thể hữu ích cho những người đang tìm kiếm giải pháp nút.


5
không chắc đó có phải là phiên bản mới của nút hay không nhưng để nó hoạt động, tôi đã phải xóa phần "getTime ()". có vẻ như _idleStart đã là một số nguyên hiện nay
kasztelan

3
Tôi vừa thử ở nút 0.10, getTime()đã bị gỡ bỏ, _idleStartđã đến lúc
Tan Nguyen

2
không hoạt động trên nút 6.3, vì cấu trúc thời gian chờ bị rò rỉ thông tin đó.
Huân,

53

CHỈNH SỬA: Tôi thực sự nghĩ rằng tôi đã tạo ra một cái thậm chí còn tốt hơn: https://stackoverflow.com/a/36389263/2378102

Tôi đã viết hàm này và tôi sử dụng nó rất nhiều:

function timer(callback, delay) {
    var id, started, remaining = delay, running

    this.start = function() {
        running = true
        started = new Date()
        id = setTimeout(callback, remaining)
    }

    this.pause = function() {
        running = false
        clearTimeout(id)
        remaining -= new Date() - started
    }

    this.getTimeLeft = function() {
        if (running) {
            this.pause()
            this.start()
        }

        return remaining
    }

    this.getStateRunning = function() {
        return running
    }

    this.start()
}

Hẹn giờ:

a = new timer(function() {
    // What ever
}, 3000)

Vì vậy, nếu bạn muốn thời gian còn lại chỉ cần làm:

a.getTimeLeft()

Cảm ơn vì điều đó. Không chính xác những gì tôi đang tìm kiếm, nhưng chức năng đếm thời gian còn lại rất hữu ích
thay vào đó,

4

Các ngăn xếp sự kiện của Javascript không hoạt động như bạn nghĩ.

Khi một sự kiện hết thời gian được tạo, nó sẽ được thêm vào hàng đợi sự kiện, nhưng các sự kiện khác có thể được ưu tiên trong khi sự kiện đó đang được kích hoạt, trì hoãn thời gian thực thi và hoãn thời gian chạy.

Ví dụ: Bạn tạo thời gian chờ với độ trễ là 10 giây để cảnh báo điều gì đó trên màn hình. Nó sẽ được thêm vào ngăn xếp sự kiện và sẽ được thực thi sau khi tất cả các sự kiện hiện tại được kích hoạt (gây ra một số độ trễ). Sau đó, khi thời gian chờ được xử lý, trình duyệt vẫn tiếp tục ghi lại các sự kiện khác để thêm chúng vào ngăn xếp, điều này gây ra sự chậm trễ hơn nữa trong quá trình xử lý. Nếu người dùng nhấp hoặc nhập nhiều ctrl +, các sự kiện của họ sẽ được ưu tiên hơn ngăn xếp hiện tại. 10 giây của bạn có thể biến thành 15 giây hoặc lâu hơn.


Nói như vậy, có rất nhiều cách để làm giả dù thời gian đã trôi qua. Một cách là thực thi setInterval ngay sau khi bạn thêm setTimeout vào ngăn xếp.

Ví dụ: Thực hiện settimeout với độ trễ 10 giây (lưu trữ độ trễ đó trong toàn cục). Sau đó, thực hiện một setInterval chạy mỗi giây để trừ đi 1 từ độ trễ và xuất ra độ trễ còn lại. Do cách ngăn xếp sự kiện có thể ảnh hưởng đến thời gian thực tế (được mô tả ở trên), điều này vẫn sẽ không chính xác, nhưng sẽ cho biết số lượng.


Trong ngắn hạn, không có cách nào thực sự để có được thời gian còn lại. Chỉ có cách để thử và truyền đạt ước tính cho người dùng.


4

Node.js phía máy chủ cụ thể

Không có cách nào ở trên thực sự hiệu quả với tôi và sau khi kiểm tra đối tượng thời gian chờ, có vẻ như mọi thứ đều tương đối so với thời điểm bắt đầu quá trình. Những điều sau đây đã làm việc cho tôi:

myTimer = setTimeout(function a(){console.log('Timer executed')},15000);

function getTimeLeft(timeout){
  console.log(Math.ceil((timeout._idleStart + timeout._idleTimeout)/1000 - process.uptime()));
}

setInterval(getTimeLeft,1000,myTimer);

Đầu ra:

14
...
3
2
1
Timer executed
-0
-1
...

node -v
v9.11.1

Đầu ra đã chỉnh sửa cho ngắn gọn, nhưng hàm cơ bản này cung cấp thời gian gần đúng cho đến khi thực thi hoặc kể từ khi thực thi. Như những người khác đã đề cập, không điều nào trong số này sẽ chính xác do cách xử lý của nút, nhưng nếu tôi muốn chặn một yêu cầu đã được chạy cách đây chưa đầy 1 phút và tôi đã lưu trữ bộ hẹn giờ, tôi không hiểu tại sao điều này lại không làm việc như một kiểm tra nhanh. Có thể thú vị khi tung hứng các đối tượng với refreshtimer trong 10.2+.


1

Bạn có thể sửa đổi setTimeout để lưu trữ thời gian kết thúc của mỗi thời gian chờ trong bản đồ và tạo một hàm được gọi getTimeoutđể tính thời gian còn lại cho thời gian chờ với một id nhất định.

Đây là siêu 's giải pháp , nhưng tôi sửa đổi nó để sử dụng một chút ít bộ nhớ

let getTimeout = (() => { // IIFE
    let _setTimeout = setTimeout, // Reference to the original setTimeout
        map = {}; // Map of all timeouts with their end times

    setTimeout = (callback, delay) => { // Modify setTimeout
        let id = _setTimeout(callback, delay); // Run the original, and store the id
        map[id] = Date.now() + delay; // Store the end time
        return id; // Return the id
    };

    return (id) => { // The actual getTimeout function
        // If there was no timeout with that id, return NaN, otherwise, return the time left clamped to 0
        return map[id] ? Math.max(map[id] - Date.now(), 0) : NaN;
    }
})();

Sử dụng:

// go home in 4 seconds
let redirectTimeout = setTimeout(() => {
    window.location.href = "/index.html";
}, 4000);

// display the time left until the redirect
setInterval(() => {
    document.querySelector("#countdown").innerHTML = `Time left until redirect ${getTimeout(redirectTimeout)}`;
},1);

Đây là phiên bản rút gọn của getTimeout IIFE này :

let getTimeout=(()=>{let t=setTimeout,e={};return setTimeout=((a,o)=>{let u=t(a,o);return e[u]=Date.now()+o,u}),t=>e[t]?Math.max(e[t]-Date.now(),0):NaN})();

Tôi hy vọng điều này cũng hữu ích cho bạn cũng như cho tôi! :)


0

Không, nhưng bạn có thể có setTimeout / setInterval của riêng mình cho hoạt ảnh trong hàm của bạn.

Giả sử câu hỏi của bạn trông giống như sau:

function myQuestion() {
  // animate the progress bar for 1 sec
  animate( "progressbar", 1000 );

  // do the question stuff
  // ...
}

Và hoạt ảnh của bạn sẽ được xử lý bởi 2 chức năng sau:

function interpolate( start, end, pos ) {
  return start + ( pos * (end - start) );
}

function animate( dom, interval, delay ) {

      interval = interval || 1000;
      delay    = delay    || 10;

  var start    = Number(new Date());

  if ( typeof dom === "string" ) {
    dom = document.getElementById( dom );
  }

  function step() {

    var now     = Number(new Date()),
        elapsed = now - start,
        pos     = elapsed / interval,
        value   = ~~interpolate( 0, 500, pos ); // 0-500px (progress bar)

    dom.style.width = value + "px";

    if ( elapsed < interval )
      setTimeout( step, delay );
  }

  setTimeout( step, delay );
}

sự khác biệt chỉ có thể được đo bằng mili giây, vì hoạt ảnh bắt đầu ở đầu hàm của bạn. Tôi thấy bạn sử dụng jQuery. Sau đó, bạn có thể sử dụng jquery.animate.
gblazex

Cách tốt nhất là bạn hãy thử và tận mắt chứng kiến. :)
gblazex

0

Nếu ai đó đang nhìn lại điều này. Tôi đã đưa ra một trình quản lý thời gian chờ và khoảng thời gian có thể giúp bạn có thời gian còn lại trong khoảng thời gian chờ hoặc khoảng thời gian cũng như thực hiện một số công việc khác. Tôi sẽ thêm vào nó để làm cho nó tiện lợi hơn và chính xác hơn, nhưng nó có vẻ hoạt động khá tốt (mặc dù tôi có thêm một số ý tưởng để làm cho nó chính xác hơn):

https://github.com/vhmth/Tock


liên kết của bạn bị hỏng
Kick Buttowski

0

Câu hỏi đã được trả lời nhưng tôi sẽ thêm một chút của mình. Nó chỉ xảy ra với tôi.

Sử dụng setTimeouttrong recursionnhư sau:

var count = -1;

function beginTimer()
{
    console.log("Counting 20 seconds");
    count++;

    if(count <20)
    {
        console.log(20-count+"seconds left");
        setTimeout(beginTimer,2000);
    }
    else
    {
        endTimer();
    }
}

function endTimer()
{
    console.log("Time is finished");
}

Tôi đoán mã là tự giải thích


0

Kiểm tra cái này:

class Timer {
  constructor(fun,delay) {
    this.timer=setTimeout(fun, delay)
    this.stamp=new Date()
  }
  get(){return ((this.timer._idleTimeout - (new Date-this.stamp))/1000) }
  clear(){return (this.stamp=null, clearTimeout(this.timer))}
}

Hẹn giờ:

let smtg = new Timer(()=>{do()}, 3000})

Nhận còn lại:

smth.get()

Xóa thời gian chờ

smth.clear()

0
    (function(){
        window.activeCountdowns = [];
        window.setCountdown = function (code, delay, callback, interval) {
            var timeout = delay;
            var timeoutId = setTimeout(function(){
                clearCountdown(timeoutId);
                return code();
            }, delay);
            window.activeCountdowns.push(timeoutId);
            setTimeout(function countdown(){
                var key = window.activeCountdowns.indexOf(timeoutId);
                if (key < 0) return;
                timeout -= interval;
                setTimeout(countdown, interval);
                return callback(timeout);
            }, interval);
            return timeoutId;
        };
        window.clearCountdown = function (timeoutId) {
            clearTimeout(timeoutId);
            var key = window.activeCountdowns.indexOf(timeoutId);
            if (key < 0) return;
            window.activeCountdowns.splice(key, 1);
        };
    })();

    //example
    var t = setCountdown(function () {
        console.log('done');
    }, 15000, function (i) {
        console.log(i / 1000);
    }, 1000);

0

Tôi đã dừng lại ở đây để tìm kiếm câu trả lời này, nhưng đã suy nghĩ kỹ vấn đề của mình. Nếu bạn ở đây vì bạn chỉ cần theo dõi thời gian trong khi quá trình setTimeout đang diễn ra, đây là một cách khác để làm điều đó:

    var focusTime = parseInt(msg.time) * 1000

    setTimeout(function() {
        alert('Nice Job Heres 5 Schrute bucks')
        clearInterval(timerInterval)
    }, focusTime)

    var timerInterval = setInterval(function(){
        focusTime -= 1000
        initTimer(focusTime / 1000)
    }, 1000);

0

Một cách nhanh hơn, dễ dàng hơn:

tmo = 1000;
start = performance.now();
setTimeout(function(){
    foo();
},tmo);

Bạn có thể nhận được thời gian còn lại với:

 timeLeft = tmo - (performance.now() - start);
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.