web-dev-qa-db-ja.com

setTimeout()に残っている時間を見つけますか?

私は自分が所有していないライブラリコードとやり取りする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 );
_

setTimeoutによって作成されたタイマーに問い合わせることで、_questionTime * 1000_に向かって進むプログレスバーを画面に配置したいと思います。唯一の問題は、これを行う方法がないようです。欠落しているgetTimeout関数はありますか?私が見つけることができるJavascriptタイムアウトに関する唯一の情報は、setTimeout( function, time)による作成とclearTimeout( id )による削除にのみ関連しています。

タイムアウトが発生するまでの残り時間、またはタイムアウトが呼び出されてからの経過時間を返す関数を探しています。私のプログレスバーコードは次のようになります:

_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:javascript setTimeout()までの残り時間を見つけるにはどうすればよいですか?


これが私が今使用しているソリューションです。私は、テストを担当するライブラリセクションを調べ、コードを解読しました(ひどく、私の許可に反して)。

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

ここに私のコードがあります:

// setTimeout 
 function mySetTimeout(func、timeout)のラッパー{
 timeouts [n = setTimeout(func、timeout)] = {
 start:new Date()。getTime( )、
 end:new Date()。getTime()+ timeout 
 t:timeout 
} 
 return n; 
}

これは、IE 6.ではないすべてのブラウザーでかなりスポットオンに機能します。

82
Just Jake

ライブラリコードを変更できない場合は、目的に合わせて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);

これは製品コードではありませんが、正しい軌道に乗せるはずです。タイムアウトごとに1つのリスナーしかバインドできないことに注意してください。私はこれで広範なテストを行っていませんが、Firebugでは動作します。

より堅牢なソリューションでは、setTimeoutをラップする同じ手法を使用しますが、代わりに、返されたtimeoutIdからリスナーへのマップを使用して、タイムアウトごとに複数のリスナーを処理します。タイムアウトがクリアされた場合にリスナーをデタッチできるように、clearTimeoutをラップすることも検討してください。

25
lawnsea

記録のために、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);
}

プリント:

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

これはFirefoxでは機能しないようですが、node.jsはjavascriptであるため、この発言はノードソリューションを探している人々に役立つと思いました。

51
Fluffy

編集:私は実際に私がさらに良いものを作ったと思う: https://stackoverflow.com/a/36389263/2378102

私はこの関数を書き、私はそれをたくさん使います:

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

タイマーを作成します。

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

したがって、残りの時間をそのままにしたい場合:

a.getTimeLeft()
47
super

おそらくこれを行うためのさらに良い方法があります。さらに、すでに記述したコードを変更する必要はありません。

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

...そして、最小化:

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

サーバー側Node.js固有

上記のどれも実際にはうまくいきませんでした。タイムアウトオブジェクトを調べたところ、すべてがプロセスの開始時と相対的であるように見えました。次は私のために働いた:

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

出力:

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

node -v
v9.11.1

簡潔にするために出力を編集しましたが、この基本機能は実行までまたは実行後のおおよその時間を提供します。他の人が言及するように、これはノードの処理方法のために正確ではありませんが、1分未満前に実行されたリクエストを抑制したい場合、タイマーを保存したので、なぜこれがしないのかわかりませんクイックチェックとして機能します。 10.2+のrefreshtimerでオブジェクトをジャグリングするのは面白いかもしれません。

3
John Watts

Javascriptのイベントスタックは、あなたが思うように動作しません。

タイムアウトイベントが作成されると、イベントキューに追加されますが、他のイベントがそのイベントの発生中に優先され、実行時間が遅延し、ランタイムが延期される場合があります。

例: 10秒の遅延でタイムアウトを作成して、画面に何かを警告します。イベントスタックに追加され、現在のすべてのイベントが発生した後に実行されます(遅延が発生します)。その後、タイムアウトが処理されると、ブラウザーは引き続き他のイベントをキャプチャし続け、それらをスタックに追加するため、処理がさらに遅延します。ユーザーがクリックするか、多数のctrl + typingを実行すると、イベントは現在のスタックよりも優先されます。 10秒は15秒以上になります。


そうは言っても、どれだけ時間が経過したかを偽造する方法はたくさんあります。 1つの方法は、スタックにsetTimeoutを追加した直後にsetIntervalを実行することです。

例: 10秒の遅延でsettimeoutを実行します(その遅延をグローバルに保存します)。次に、毎秒実行されるsetIntervalを実行して、遅延から1を減算し、残りの遅延を出力します。イベントスタックが実際の時間に影響を与える可能性があるため(上​​記を参照)、これはまだ正確ではありませんが、カウントを提供します。


要するに、残りの時間を取得する実際の方法はありません。ユーザーに推定値を伝える方法はあります。

3
vol7ron

質問は既に回答済みですが、少し追加します。それはちょうど私に起こった。

次のように、setTimeoutrecursionを使用します。

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

コードは自明だと思います

0
dasfdsa

これをチェックしてください:

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

タイマーを作成します。

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

残ります:

smth.get()

タイムアウトをクリア

smth.clear()
0
crokudripi

いいえ。ただし、関数のアニメーションに独自のsetTimeout/setIntervalを設定できます。

質問は次のようになります。

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

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

そして、アニメーションは次の2つの関数によって処理されます。

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

私はここでこの答えを探して立ち寄ったが、私の問題を考えすぎていた。 setTimeoutの進行中に時間を追跡する必要があるためにここにいる場合は、別の方法を使用します。

    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
Jbur43

modifysetTimeoutを使用して、各タイムアウトの終了時刻をマップに保存し、という関数を作成できます。 getTimeoutは、特定のIDのタイムアウトの残り時間を取得します。

これは supersolution でしたが、わずかに少ないメモリを使用するように変更しました

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

使用法:

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

これは、このgetTimeout[〜#〜] iife [〜#〜] の縮小版です。

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

これが私にとってもあなたにとっても有益であることを願っています! :)

0
Kimeiga

誰かがこれを振り返っているなら。私は、タイムアウトとインターバルの残り時間を取得できるほか、他のことを行うことができるタイムアウトとインターバルマネージャーを開発しました。私はそれをより洗練されてより正確にするために追加しますが、それはかなりうまくいくようです(さらに正確にするためのアイデアがいくつかありますが):

https://github.com/vhmth/Tock

0
Vinay
    (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
Vitalik s2pac