4

私は賢くなり、独自の待機関数を作成しようと考えました (これを行うには他の方法があることに気付きました)。だから私は書いた:

var interval_id;
var countdowntimer = 0;

function Wait(wait_interval) {
  countdowntimer = wait_interval;

  interval_id = setInterval(function() {
    --countdowntimer <=0 ? clearInterval(interval_id) : null;
  }, 1000);

  do {} while (countdowntimer >= 0);
}

// Wait a bit: 5 secs
Wait(5);

無限ループを除いて、これはすべて機能します。調べてみると、While ループを取り出すと、予想どおり匿名関数が 5 回入力されます。したがって、明らかにグローバル変数countdowntimerが減少します。

ただし、 countdowntimerの値を確認すると、While ループではダウンすることはありません。これは、無名関数が While ループで呼び出されているにもかかわらずです!

明らかに、どういうわけか、countdowntimerの 2 つの値が浮かんでいますが、なぜでしょうか?

編集

わかりましたので、Javascript がシングル スレッドであることを (今では) 理解しています。そして、それは - 一種の - 私の質問に答えます。しかし、この単一スレッドの処理のどの時点で、 setIntervalを使用したいわゆる非同期呼び出しが実際に行われるのでしょうか? 関数呼び出しの間だけですか?そうではありません。実行に時間がかかる関数はどうでしょうか。

4

4 に答える 4

5

変数のコピーが 2 つ存在するわけではありません。Web ブラウザーの Javascript はシングル スレッドです (新しい Web ワーカーを使用しない限り)。したがって、無名関数は実行する機会がありません。これWaitは、インタープリターが拘束されているためです。

ブラウザーベースの Javascript でビジー待機関数を使用することはできません。他には何も起こりません (そして、他のほとんどの環境では、たとえ可能であっても、それは悪い考えです)。代わりにコールバックを使用する必要があります。これをミニマリストにリワークすると次のようになります。

var interval_id;
var countdowntimer = 0;

function Wait(wait_interval, callback) {
    countdowntimer = wait_interval;

    interval_id = setInterval(function() {
        if (--countdowntimer <=0) {
            clearInterval(interval_id);
            interval_id = 0;
            callback();
        }
    }, 1000);
}

// Wait a bit: 5 secs
Wait(5, function() {
    alert("Done waiting");
});

// Any code here happens immediately, it doesn't wait for the callback

あなたのフォローアップに答える編集:

しかし、この単一スレッドの処理のどの時点で、setInterval を使用したいわゆる非同期呼び出しが実際に行われるのでしょうか? 関数呼び出しの間だけですか?そうではありません。実行に時間がかかる関数はどうでしょうか。

そのため、関数が長時間実行されないようにすることが重要です。(技術的には、関数呼び出し間でさえありません。他の 3 つの関数を呼び出す関数がある場合、その (外部) 関数が実行されている間、インタープリターは他に何もできません。) インタープリターは基本的に、必要な関数のキューを維持します。実行します。グローバルコードを実行することから始まります(大きな関数呼び出しのように)。次に、何かが起こると (ユーザー入力イベント、経由でスケジュールされたコールバックを呼び出す時間にsetTimeout達するなど)、インタープリターは必要な呼び出しをキューにプッシュします。それは常にキューの先頭で呼び出しを処理するため、物事が積み重なる可能性があります(あなたのsetInterval呼び出しのようにsetInterval少し特殊 — 前のコールバックがまだ処理待ちのキューにある場合、後続のコールバックはキューに入れられません)。したがって、コードがいつ制御を取得し、いつ制御を解放するか (たとえば、戻ることによって) について考えてみてください。インタープリターは、ユーザーが制御を解放した後、制御を再びユーザーに返す前にのみ、他のことを実行できます。繰り返しになりますが、一部のブラウザー (IE など) では、同じスレッドが UI の描画などにも使用されるため、DOM 挿入 (たとえば) は、制御をブラウザーに戻して取得できるようになるまで表示されません。その絵を描いています。

Web ブラウザーで Javascript を使用する場合、ソリューションの設計とコーディングにイベント駆動型のアプローチを採用する必要があります。典型的な例は、ユーザーに情報を求めるプロンプトです。非イベント駆動型の世界では、次のことができます。

// Non-functional non-event-driven pseudo-example
askTheQuestion();
answer = readTheAnswer();      // Script pauses here
doSomethingWithAnswer(answer); // This doesn't happen until we have an answer
doSomethingElse();

それはイベント駆動型の世界では機能しません。代わりに、次のようにします。

askTheQuestion();
setCallbackForQuestionAnsweredEvent(doSomethingWithAnswer);
// If we had code here, it would happen *immediately*,
// it wouldn't wait for the answer

たとえば、askTheQuestionページ上の div をオーバーレイして、さまざまな情報を入力するようにユーザーに促すフィールドを配置し、完了したらクリックする [OK] ボタンを表示することができます。setCallbackForQuestionAnswered実際clickには「OK」ボタンでイベントをフックします。doSomethingWithAnswerフィールドから情報を収集し、div を削除または非表示にし、その情報で何かを行います。

于 2010-05-18T14:07:55.903 に答える
3

ほとんどの Javascript 実装はシングル スレッドwhileであるため、ループを実行しているときは他の処理を実行できないため、 が実行されintervalている間はwhileが実行されないため、無限ループが発生します。

javascript でスリープ/待機/一時停止関数を作成する同様の試みが多数ありますが、ほとんどの実装はシングル スレッドであるため、スリープ中 (!) に他のことを行うことはできません。

遅延を作成する別の方法は、タイムアウトを記述することです。コードのチャンクの実行を延期できますが、多くの関数でそれを分割する必要があります。いつでも関数をインライン化できるため、簡単に追跡できます (また、同じ実行コンテキスト内で変数を共有できます)。

これをより読みやすくするために、javascript に構文糖衣を追加するライブラリもいくつかあります。

編集: John Resig自身によるHow javascript timers workに関する優れた ブログ投稿があります。かなり詳しく説明してくれます。それが役に立てば幸い。

于 2010-05-18T14:09:08.353 に答える
2

実際には、javascript がシングルスレッドであるため、ループが実行されている間は interval 関数が実行されないことがほぼ保証されています。

Waitこれまで誰も作ったことがなかった(そして多くの人が試した) のには理由があります。それは単にできません。

関数をビットに分割し、setTimeout または setInterval を使用してこれらをスケジュールする必要があります。

//first part
...
setTimeout(function(){
    //next part
}, 5000/*ms*/);

必要に応じて、これをステート マシンとして実装できます (実装する必要があります)。

于 2010-05-18T14:10:03.140 に答える
0

グローバル countdowntimer 変数を使用する代わりに、代わりに setInterval のミリ秒属性を変更してみませんか? 何かのようなもの:

var waitId;

function Wait(waitSeconds)
{
    waitId= setInterval(function(){clearInterval(waitId);}, waitSeconds * 1000);
}
于 2010-05-18T14:09:09.620 に答える