1. ホーム
  2. javascript

[解決済み] setTimeout()の残り時間は?

2023-01-05 23:11:41

質問

私は、私が所有しておらず、(合理的に)変更することができないライブラリコードと相互作用するいくつかの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 );

画面上にプログレスバーを表示したい。 questionTime * 1000 で作成されたタイマーを照会して setTimeout . 唯一の問題は、これを行う方法がないように思われることです。 このような 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のラッパー
関数 mySetTimeout( func, timeout ) {
    タイムアウト[ n = setTimeout( func, timeout ) ] = { }.
        start: new Date().getTime(),
        end: new Date().getTime() + タイムアウト。
        t: タイムアウト
    
    return n;
}

これは、IE 6 以外のどのブラウザでも、かなり正確に動作します。 オリジナルの iPhone でさえ、私は物事が非同期的になることを期待していました。

どのように解決するのですか?

ライブラリのコードを修正できない場合、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をラップすることを検討することもできます。