1. ホーム
  2. javascript

[解決済み] setTimeout(fn, 0)が役に立つことがあるのはなぜですか?

2022-03-18 08:38:22

質問

最近、かなり厄介なバグに遭遇しました。 <select> JavaScriptで動的に この動的に読み込まれる <select> はあらかじめ選択された値を持っていました。 IE6 では、すでに選択された <option> というのは、時々 <select> 's selectedIndex の値は、選択された <option> 's index 属性は、以下のようになります。

field.selectedIndex = element.index;

ところが、このコードはうまくいかなかった。 たとえフィールドの selectedIndex は正しく設定されているのに、間違ったインデックスが選択されてしまうのです。 しかし alert() ステートメントを適切なタイミングで挿入すると、正しいオプションが選択されました。 これはタイミングの問題かもしれないと思い、以前コードで見たことのあるようなランダムなものを試してみました。

var wrapFn = (function() {
    var myField = field;
    var myElement = element;

    return function() {
        myField.selectedIndex = myElement.index;
    }
})();
setTimeout(wrapFn, 0);

そして、これがうまくいったのです

問題は解決したのですが、なぜこれで問題が解決したのかがはっきりしないのが不安です。 どなたか公式な説明をお持ちの方はいらっしゃいますか? を使って関数を呼び出すことで、どのようなブラウザの問題を回避できるのでしょうか? setTimeout() ?

解決方法は?

質問の中に レースコンディション の間にある。

  1. ブラウザがドロップダウンリストを初期化しようとし、選択されたインデックスが更新される準備ができたこと、および
  2. 選択インデックスを設定するコード

あなたのコードは常にこの競争に勝ち続け、ブラウザの準備が整う前にドロップダウンの選択を設定しようとし、バグが表示されることを意味しました。

このレースが存在したのは、JavaScriptが 実行の単一スレッド は、ページレンダリングと共有されています。事実上、JavaScript を実行すると、DOM の更新がブロックされます。

あなたの回避策は

setTimeout(callback, 0)

起動時 setTimeout をコールバックとし、2番目の引数に0を指定すると、コールバックが実行されるようスケジュールされます。 非同期 タブにフォーカスがあり、JavaScript の実行スレッドがビジー状態でない場合は、10ms 程度の最短時間後になります。

そのため、OPの解決策は、選択したインデックスの設定を10msほど遅らせることでした。これにより、ブラウザはDOMを初期化する機会を得て、バグを修正することができました。

Internet Explorerはどのバージョンも癖のある動作をするので、このような回避策が必要な場合もありました。あるいは、OPのコードベースにおける本物のバグであったかもしれません。


Philip Robertsのトークを参照 イベントループとは一体何なのか? を使うと、より丁寧な説明ができます。