3

Javascriptでタイマーがどのように機能するかよくわかりません。JS はシングル スレッドであるため、コードが実行され、たとえばユーザーがボタンをクリックすると、クリックがキューに追加されます。キューは空だったので、キューは次のようになります。

[クリック]

現在実行中のコードが終了するとすぐに実行されますよね?同じコードがまだ実行されていて、まだ完了していないとしましょうsetTimeout(fn,3000)

今、私はこれが正しいかどうかわかりません。これfnはキューに追加されませんが、この瞬間から 3000 ミリ秒近くの時点で発火します (このコードの実行が終了した時点からではありません)。この時点で、そのイベントが発生する必要がある場合 (今から約 3000 ミリ秒後) 他のコードが実行されている場合 (上記の [クリック] など)、これfnはキューの最後に配置されます。

元の実行中のコードに戻ると、[クリック] はキューにあり、コードはさらに実行されます。実行中のコードは、DOM の一部の要素のスタイル プロパティを変更し、境界線を追加します。この変更は、ブラウザが UI を更新するときに行う必要があります。JS が他のコードを実行しているため、すぐには表示されず、クリック後に追加されるため、キューは次のようになります。

[クリック] [UI の更新 - 境界線が変更される可能性があります]

そのため、キューには、現在実行中の JS が終了したときに呼び出される 2 つのイベントが含まれています。[クリック] が呼び出されますが、以前のコードで要求された境界線の変更はユーザーには表示されません。クリックが完了すると、UI の更新であるキューからの次のイベントがジャンプします。以前のコードを実行したときに要求した境界線の変更を含め、おそらくたくさんの描画が行われるでしょう。

クリック イベントが実行されている間、または UI が表示に変更を加えて画面を再描画しているときに、スケジュールしたタイマーが起動fnし、キューに追加され、できるだけ早く実行されます。

私の理解は正しいですか?私が何かを誤解したかどうかを誰かが明確にし、どこが間違っているかを説明できれば、それは素晴らしいことです. これを明確にしたら、この質問をsetTimeout(fn,0)トリックに拡張します。これは、実際に私をさらに混乱させるものです。

4

1 に答える 1

2

イベントループの仕組みについては正しいです。これは、実行がスケジュールされている一連のタスク (または関数) とそれらを実行するループがあるシングルスレッド環境です。

誰かが示唆したように、UI の更新アクションはこのループの外ではありません。ただし、別のタスクとしてスケジュールされていません。これは、クリック ハンドラーの一部として同期的に実行されます。

[click [UI action] ]

同期/非同期

同期アクションは、コードの実行が完了するまでブロックするアクションです。つまり、JavaScript エンジンに対して (「境界線を引く」) と指示すると、コードの次の行が実行される前に境界線が描画されるようになります。これは、次の例で簡単に確認できます。

 $(".suggest-edit-post").css('background-color', 'red');
 $(".suggest-edit-post").css('background-color');// 'red'

したがって、DOM 操作は、JavaScript における数少ない同期アクションの 1 つです。その他のそのようなアクションはalertconfirmポップアップを開くメソッドです。

通常、関数にコールバック パラメータ (またはコールバック関数を渡すためのその他のメカニズム) があるかどうかを観察することで、関数が非同期であると推測できます。

たとえば、ajax()関数は非同期であるため、クリック時に呼び出すと次のようになります。

[click[ajax.get]]           [ajax.success]
|       |       |       |       |       |       |       |       |     
1               2               3               4               5

を実行するgetと、他の処理を実行できる一時停止があり、応答が到着してコールバックが実行されます。次に、独自のコールバックなどを持つ他のリクエストを作成できます。

setTimeout(fn,0)

このすべて (イベント ループ) は、ランタイムによって処理されます。これにはいくつかの良い面と悪い面があります。スレッド プロセスなどを処理する必要がないため、良いことです。たとえば、ajax 呼び出しに対する応答を受け取った後、何をしたいのかを指定するだけで済みます。ランタイムはそれを実現するだけです。

また、実際に一時停止して、どのアクションがその後に発生するかをスケジュールすることもできないため、悪いことでもあります。タスクの実行が開始されると、次のタスクが開始される前に最後まで実行する必要があります。

同時に発生することが多くない場合、これは問題ではありません。

[click[ajax.get]]               [ajax.success]
|       |       |       |       |       |       |       |       |     
1               2               3               4               5

この場合、success関数は応答が到着した瞬間に実行されます。

しかし、非常に複雑なデータ操作やアニメーションを一時停止中に実行するようにスケジュールするとどうなるでしょうか。

[click[ajax.get]] [complex data manipulation/animation] [ajax.success]
|       |       |       |       |       |       |       |       |     
1               2               3               4               5

success関数 (ユーザーにいくつかの重要な情報を表示している可能性があります) の実行を延期して他の関数を優先することは望ましくないかもしれませんが、タスクを再配置したり優先順位を付けたりすることはできないため、この問題の解決策はありません。 .

そのため、遅れる場合もあります。わかりましたが、どのくらい遅れていますか?エンジンは時間通りに実行しようとするため、タスクを待機する必要がある 時間は、最大のタスクの所要時間と同じです。したがって、すべてのタスクが短ければ問題ありません。

setTimeout(fn,0)ループでより適切に処理されるいくつかの短いタスクにタスクを分割できます。

[click[ajax.get]] [animation] [ajax.success] [animation] [animation] 
|       |       |       |       |       |       |       |       |     
1               2               3               4               5

一連の HTML 要素を新しいデータで更新する必要があるとしましょう:

new_values.forEach(function(val, index){
  elements[index].text(val)
})

これを別のタスクで行うには、次のようにします。

new_values.forEach(function(val, index){
  setTimeout(function(){
      elements[index].text(val)
  }, 0)
})

1 つの大きなタスクの代わりに、小さなタスクがたくさんあります。

PSそれはの唯一の正当な使用ですsetTimeout(fn,0)

于 2015-01-17T16:15:51.953 に答える