查找setTimeout()中剩下的時間?

[英]find the time left in a setTimeout()?


I'm writing some Javascript that interacts with library code that I don't own, and can't (reasonably) change. It creates Javascript timeouts used for showing the next question in a series of time-limited questions. This isn't real code because it is obfuscated beyond all hope. Here's what the library is doing:

我正在編寫一些Javascript,這些Javascript與我不擁有的庫代碼交互,並且不能(合理地)更改。它創建用於在一系列時間限制問題中顯示下一個問題的Javascript超時。這不是真正的代碼,因為它被混淆得超出了所有的希望。圖書館的工作是:

....
// 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 );

I want to put a progress bar onscreen that fills towards questionTime * 1000 by interrogating the timer created by setTimeout. The only problem is, there seems to be no way to do this. Is there a getTimeout function that I'm missing? The only information on Javascript timeouts that I can find is related only to creation via setTimeout( function, time) and deletion via clearTimeout( id ).

我想在屏幕上放置一個進度條,通過詢問setTimeout創建的計時器來填充questionTime * 1000。唯一的問題是,似乎沒有辦法做到這一點。有一個getTimeout函數我漏掉了嗎?我能找到的關於Javascript超時的唯一信息只與通過setTimeout(函數,時間)創建和通過clearTimeout(id)刪除有關。

I'm looking for a function that returns either the time remaining before a timeout fires, or the time elapsed after a timeout has been called. My progress bar code looks like this:

我正在尋找一個函數,該函數返回超時觸發前剩余的時間,或者調用超時后經過的時間。我的進度條代碼是這樣的:

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: How do I find the time remaining before a javascript setTimeout()?

tl;dr:如何找到javascript setTimeout()之前的剩余時間?


Here's the solution I'm using now. I went through the library section that's in charge of tests, and unscrambled the code (terrible, and against my permissions).

這是我現在用的方法。我瀏覽了負責測試的library部分,並對代碼進行了解密(很糟糕,而且違反了我的權限)。

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

and here's my code:

這是我的代碼:

// wrapper for setTimeout
function mySetTimeout( func, timeout ) {
    timeouts[ n = setTimeout( func, timeout ) ] = {
        start: new Date().getTime(),
        end: new Date().getTime() + timeout
        t: timeout
    }
    return n;
}

This works pretty spot-on in any browser that isn't IE 6. Even the original iPhone, where I expected things to get asynchronous.

這在IE 6之外的任何瀏覽器中都很有效。甚至是最初的iPhone,我希望它能實現異步。

10 个解决方案

#1


24  

If you can't modify the library code, you'll need to redefine setTimeout to suit your purposes. Here's an example of what you could do:

如果不能修改庫代碼,則需要重新定義setTimeout以適應您的目的。這里有一個你可以做的例子:

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

This is not production code, but it should put you on the right track. Note that you can only bind one listener per timeout. I haven't done extensive testing with this, but it works in Firebug.

這不是生產代碼,但它應該使您走上正確的軌道。注意,每次超時只能綁定一個偵聽器。我還沒有做過大量的測試,但是它在Firebug中工作。

A more robust solution would use the same technique of wrapping setTimeout, but instead use a map from the returned timeoutId to listeners to handle multiple listeners per timeout. You might also consider wrapping clearTimeout so you can detach your listener if the timeout is cleared.

更健壯的解決方案將使用包裝setTimeout的相同技術,而是使用從返回的timeoutId到偵聽器的映射來處理每個超時的多個偵聽器。您還可以考慮包裝clearTimeout,以便在超時被清除時可以分離偵聽器。

#2


47  

Just for the record, there is a way to get the time left in node.js:

僅就記錄而言,有一種方法可以在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);
}

Prints:

打印:

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

This doesn't seem to work in firefox through, but since node.js is javascript, I thought this remark might be helpful for people looking for the node solution.

這在firefox中似乎行不通,但是從node開始。js是javascript,我認為這句話可能對尋找節點解決方案的人有幫助。

#3


40  

EDIT: I actually think I made an even better one: https://stackoverflow.com/a/36389263/2378102

編輯:我認為我做了一個更好的:https://stackoverflow.com/a/36389263/2378102

I wrote this function and I use it a lot:

我寫了這個函數,我經常用它:

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

Make a timer:

定時器:

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

So if you want the time remaining just do:

所以如果你想要剩下的時間,只要:

a.getTimeLeft()

#4


3  

Javascript's event stacks don't operate how you would think.

Javascript的事件堆棧不能按照您的想法操作。

When a timeout event is created, it is added to the event queue, but other events may take priority while that event is being fired, delay the execution time and postponing runtime.

當創建超時事件時,會將其添加到事件隊列中,但是在觸發該事件時,其他事件可能具有優先級,延遲執行時間和延遲運行時。

Example: You create a timeout with a delay of 10 seconds to alert something to the screen. It will be added to the event stack and will be executed after all current events are fired (causing some delay). Then, when the timeout is processed, the browser still continues to capture other events add them to the stack, which causes further delays in the processing. If the user clicks, or does a lot of ctrl+typing, their events take priority over the current stack. Your 10 seconds can turn into 15 seconds, or longer.

示例:創建一個延遲為10秒的超時,以向屏幕發出警報。它將被添加到事件堆棧中,並在觸發所有當前事件(導致一些延遲)之后執行。然后,當處理超時時,瀏覽器仍然會繼續捕獲其他事件,並將它們添加到堆棧中,從而導致處理過程中的進一步延遲。如果用戶單擊或執行大量的ctrl+鍵入操作,那么他們的事件將優先於當前堆棧。你的10秒可以變成15秒,甚至更長。


That being said, there are many ways to fake how much time has passed. One way is to execute a setInterval right after you add the setTimeout to the stack.

也就是說,有很多方法可以假裝時間已經過去了多少。一種方法是在向堆棧添加setTimeout之后立即執行setInterval。

Example: Perform a settimeout with a 10 second delay (store that delay in a global). Then perform a setInterval that runs every second to subtract 1 from the delay and output the delay remaining. Because of how the event stack can influence actual time (described above), this still won't be accurate, but does give a count.

示例:執行一個具有10秒延遲的settimeout(將該延遲存儲在全局中)。然后執行每秒鍾運行一次的setInterval,從延遲中減去1並輸出剩余的延遲。由於事件堆棧如何影響實際時間(如上所述),這仍然不會是准確的,但是會給出一個計數。


In short, there is no real way to get the remaining time. There are only ways to try and convey an estimate to the user.

簡而言之,沒有真正的方法來獲得剩余的時間。只有一些方法可以嘗試並向用戶傳達評估。

#5


1  

Here is maybe an even better way to do it, plus, you won't need to change code you've already written:

這里有一個更好的方法,另外,你不需要改變你已經寫過的代碼:

var getTimeout = (function() { // IIFE
    var _setTimeout = setTimeout, // Reference to the original setTimeout
        map = {}; // Map of all timeouts with their start date and delay

    setTimeout = function(callback, delay) { // Modify setTimeout
        var id = _setTimeout(callback, delay); // Run the original, and store the id

        map[id] = [Date.now(), delay]; // Store the start date and delay

        return id; // Return the id
    };

    return function(id) { // The actual getTimeLeft function
        var m = map[id]; // Find the timeout in map

        // If there was no timeout with that id, return NaN, otherwise, return the time left clamped to 0
        return m ? Math.max(m[1] - Date.now() + m[0], 0) : NaN;
    }
})();

... And mimimized:

…和最小化:

var getTimeout=function(){var e=setTimeout,b={};setTimeout=function(a,c){var d=e(a,c);b[d]=[Date.now(),c];return d};return function(a){return(a=b[a])?Math.max(a[1]-Date.now()+a[0],0):NaN}}();

#6


0  

No, but you can have your own setTimeout/setInterval for animation in your function.

沒有,但是您可以在函數中為動畫設置自己的setTimeout/setInterval。

Say your question looks like this:

假設你的問題是這樣的:

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

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

And your animation will be handled by these 2 functions:

你的動畫將由這兩個功能來處理:

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

#7


0  

If anyone's looking back on this. I've come out with a timeout and interval manager that can get you the time left in a timeout or interval as well as do some other stuff. I'll be adding to it to make it more nifty and more accurate, but it seems to work fairly well as is (although I have some more ideas to make it even more accurate):

如果有人回頭看的話。我推出了一個超時和間隔管理器,它可以讓你在超時或間隔中獲得剩余的時間,還可以做一些其他的事情。我將添加它以使它更俏皮和更准確,但它似乎工作得相當不錯(盡管我有更多的想法使它更准確):

https://github.com/vhmth/Tock

https://github.com/vhmth/Tock

#8


0  

Question has already been answered but I will add my bit. It just occured to me.

問題已經得到了回答,但我要補充一點。我突然想到。

Use setTimeout in recursion as follows:

在遞歸中使用setTimeout:

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

I guess the code is self explanatory

我想代碼是不言自明的

#9


0  

Check this one:

檢查這一個:

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

Make a timer:

定時器:

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

Get remain:

保持:

smth.get()

Clear timeout

清晰的超時

smth.clear()

#10


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

注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2010/06/29/4f50f514f6d5c6e8b044d2a98bc91956.html



 
粤ICP备14056181号  © 2014-2020 ITdaan.com