私は自分が所有していないライブラリコードとやり取りする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.ではないすべてのブラウザーでかなりスポットオンに機能します。
ライブラリコードを変更できない場合は、目的に合わせて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をラップすることも検討してください。
記録のために、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であるため、この発言はノードソリューションを探している人々に役立つと思いました。
編集:私は実際に私がさらに良いものを作ったと思う: 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()
おそらくこれを行うためのさらに良い方法があります。さらに、すでに記述したコードを変更する必要はありません。
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}}();
サーバー側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でオブジェクトをジャグリングするのは面白いかもしれません。
Javascriptのイベントスタックは、あなたが思うように動作しません。
タイムアウトイベントが作成されると、イベントキューに追加されますが、他のイベントがそのイベントの発生中に優先され、実行時間が遅延し、ランタイムが延期される場合があります。
例: 10秒の遅延でタイムアウトを作成して、画面に何かを警告します。イベントスタックに追加され、現在のすべてのイベントが発生した後に実行されます(遅延が発生します)。その後、タイムアウトが処理されると、ブラウザーは引き続き他のイベントをキャプチャし続け、それらをスタックに追加するため、処理がさらに遅延します。ユーザーがクリックするか、多数のctrl + typingを実行すると、イベントは現在のスタックよりも優先されます。 10秒は15秒以上になります。
そうは言っても、どれだけ時間が経過したかを偽造する方法はたくさんあります。 1つの方法は、スタックにsetTimeoutを追加した直後にsetIntervalを実行することです。
例: 10秒の遅延でsettimeoutを実行します(その遅延をグローバルに保存します)。次に、毎秒実行されるsetIntervalを実行して、遅延から1を減算し、残りの遅延を出力します。イベントスタックが実際の時間に影響を与える可能性があるため(上記を参照)、これはまだ正確ではありませんが、カウントを提供します。
要するに、残りの時間を取得する実際の方法はありません。ユーザーに推定値を伝える方法はあります。
質問は既に回答済みですが、少し追加します。それはちょうど私に起こった。
次のように、setTimeout
でrecursion
を使用します。
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");
}
コードは自明だと思います
これをチェックしてください:
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()
いいえ。ただし、関数のアニメーションに独自の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 );
}
私はここでこの答えを探して立ち寄ったが、私の問題を考えすぎていた。 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);
modifysetTimeout
を使用して、各タイムアウトの終了時刻をマップに保存し、という関数を作成できます。 getTimeout
は、特定のIDのタイムアウトの残り時間を取得します。
これは super の solution でしたが、わずかに少ないメモリを使用するように変更しました
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})();
これが私にとってもあなたにとっても有益であることを願っています! :)
誰かがこれを振り返っているなら。私は、タイムアウトとインターバルの残り時間を取得できるほか、他のことを行うことができるタイムアウトとインターバルマネージャーを開発しました。私はそれをより洗練されてより正確にするために追加しますが、それはかなりうまくいくようです(さらに正確にするためのアイデアがいくつかありますが):
(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);