ホームページ >ウェブフロントエンド >jsチュートリアル >js setTimeOut() アプリケーション
setTimeoutは、フロントエンドエンジニアが扱わなければならない関数です。見た目は非常にシンプルでシンプルです。タイマーという非常に珍しい名前が付いています。若い頃は、未来をコントロールできると素朴に考えていました。しかし、実際にはそうではありませんでした。 「なんて簡単なことだろう。そこには衝撃的な秘密が隠されている。初めてこの機能を使ったとき、JSでマルチスレッドを実現するツールだと素朴に思ったのを今でも覚えている。当時はこれを使っていた」戦車戦に関する小さなゲームを実装することになり、プレイするのは大変でした。それは楽しいです。しかし、フロントエンドロードをさらに進めていくと、その理解が変わり始めます。ベールに覆われているようですと、理解できない奇妙な行動がしばしばあります。ついに忍耐力が限界に達し、私はその仮面を剥がして調べてみることにしました。
起源について話すにはsetTimeout については、公式の定義から始める必要があります。w3c では次のように定義されています。
setTimeout() メソッドは、指定されたミリ秒数の後に関数または計算式を呼び出すために使用されます。
このような説明を見ると、これがタイマーであり、設定した機能が時間切れになると実行される「目覚まし時計」であることがわかります。 , このように考えずにはいられません 質問です。settimeout(fn,0) の場合はどうなりますか? 定義によると、すぐに実行されますか? 真実をテストするための唯一の基準は実践です。次の実験
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <script> alert(1); setTimeout("alert(2)", 0); alert(3); </script> </body> </html>
これは非常に単純な実験で、settimeout(0)がすぐに実行される場合、ここでの実行結果は1->2>3となるはずですが、実際の結果は1です。 ->3->2. これは、settimeout (0) がすぐに実行されないことを示しています。同時に、settimeout の動作について非常に奇妙に感じます。
JS エンジンが実行されます。単一のスレッドで
# まずは上記の問題に対処しましょう。それは脇に置いて、js 言語の設計から何か手がかりが見つかるかどうか見てみましょう。
私たちは、非常に重要な問題を発見しました。 js言語の設計で重要な点は、jsにはマルチスレッドがないということです。jsエンジンの実行はシングルスレッドで実行されます。この機能は長い間私を悩ませていました。js以来、それを理解できませんでしたシングルスレッドです。誰がタイマーを計りますか? 誰が ajax リクエストを送信しますか? 私は盲点に陥りました。つまり、js はブラウザーに相当します。私たちはブラウザーでコードを実行することに慣れていますが、ブラウザ自体。js エンジンはシングルスレッドですが、ブラウザはマルチスレッドにすることができます。js エンジンはブラウザの単なるスレッドです。タイマーのタイミング、ネットワーク リクエスト、ブラウザのレンダリングなどはすべて、異なるスレッドによって完了します。証拠があれば、引き続き例を見てみましょう
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> </body> <script> var isEnd = true; window.setTimeout(function () { isEnd = false;//1s后,改变isEnd的值 }, 1000); while (isEnd); alert('end'); </script> </html>
isEnd はデフォルトで true であり、while の無限ループになります。最終的に、アラートは実行されません。タイマーを追加し、isEnd を 1 の後に false に変更しましたjs エンジンがマルチスレッドの場合、アラートは 1 秒後に実行されますが、実際の状況では、ページが永遠にループし、アラートは実行されません。これは、settimeout を次のように使用できないことの良い証拠です。マルチスレッド。JS エンジンの実行はシングルスレッドです。
#イベント ループ
上記の実験から、settimeout が正確に何をするのか、さらに混乱します。 ?js 言語の設計から答えを見つける必要があることがわかりました。 js エンジンは単一のスレッドで実行されます。イベント駆動型言語をベースにしており、その実行順序はイベントキューと呼ばれる仕組みに従っています 図からわかるように、ブラウザにはイベントトリガー、ネットワークリクエスト、タイマーなどのさまざまなスレッドが存在します スレッド間の接続すべてはイベントに基づいています. js エンジンが他のスレッドに関連するコードを処理すると、他のスレッドに配布されます. 処理された後、js が必要になります. エンジンが計算するときに、イベント キューにタスクを追加します. この間、プロセスでは、js はコードをブロックせず、他のスレッドの実行が完了するのを待ちます。他のスレッドの実行が完了すると、イベント タスクが追加されて、関連する操作を実行するように js エンジンに指示します。これが、js モデルの非同期プログラミングです。settimeout(0) を振り返ると、ここで js コードが実行されると、タイマー スレッドが開始され、次のコードが実行され続けることに突然気づきます。指定された時刻に実行されます 次に、イベント キューにタスクを挿入します。settimeout(0) の操作がすべてのメイン スレッド タスクの後に配置されることがわかります。これは、最初の実験結果が 1->3- である理由も説明します2. settimeout の公式定義はわかりにくいため、新しい定義を指定する必要があります: 指定された時間内に、タスクをイベント キューに追加し、アイドル状態になった後に js エンジンが実行されるのを待ちます。
js エンジンと GUI エンジンは相互に排他的です。
これについて言えば、ブラウザの別のエンジンである GUI レンダリング エンジンについて話しましょう。js でのレンダリング操作も非同期です。たとえば、DOM オペレーション コードはイベント キューにタスクを生成します。 js がこのタスクを実行すると、GUI エンジンのレンダリングが呼び出されます。js语言设定js引擎与GUI引擎是互斥的,也就是说GUI引擎在渲染时会阻塞js引擎计算.原因很简单,如果在GUI渲染的时候,js改变了dom,那么就会造成渲染不同步. 我们需要深刻理解js引擎与GUI引擎的关系,因为这与我们平时开发息息相关,我们时长会遇到一些很奇葩的渲染问题.看这个例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <table border=1> <tr><td><button id='do'>Do long calc - bad status!</button></td> <td><p id='status'>Not Calculating yet.</p></td> </tr> <tr><td><button id='do_ok'>Do long calc - good status!</button></td> <td><p id='status_ok'>Not Calculating yet.</p></td> </tr> </table> <script> function long_running(status_p) { var result = 0; for (var i = 0; i < 1000; i++) { for (var j = 0; j < 700; j++) { for (var k = 0; k < 300; k++) { result = result + i + j + k; } } } document.querySelector(status_p).innerHTML = 'calclation done' ; } document.querySelector('#do').onclick = function () { document.querySelector('#status').innerHTML = 'calculating....'; long_running('#status'); }; document.querySelector('#do_ok').onclick = function () { document.querySelector('#status_ok').innerHTML = 'calculating....'; window.setTimeout(function (){ long_running('#status_ok') }, 0); }; </script> </body> </html>
我们希望能看到计算的每一个过程,我们在程序开始,计算,结束时,都执行了一个dom操作,插入了代表当前状态的字符串,Not Calculating yet.和calculating....和calclation done.计算中是一个耗时的3重for循环. 在没有使用settimeout的时候,执行结果是由Not Calculating yet 直接跳到了calclation done.这显然不是我们希望的.而造成这样结果的原因正是js的事件循环单线程机制.dom操作是异步的,for循环计算是同步的.异步操作都会被延迟到同步计算之后执行.也就是代码的执行顺序变了.calculating....和calclation done的dom操作都被放到事件队列后面而且紧跟在一起,造成了丢帧.无法实时的反应.这个例子也告诉了我们,在需要实时反馈的操作,如渲染等,和其他相关同步的代码,要么一起同步,要么一起异步才能保证代码的执行顺序.在js中,就只能让同步代码也异步.即给for计算加上settimeo0t.
settimeout(0)的作用
不同浏览器的实现情况不同,HTML5定义的最小时间间隔是4毫秒. 使用settimeout(0)会使用浏览器支持的最小时间间隔.所以当我们需要把一些操作放到下一帧处理的时候,我们通常使用settimeout(0)来hack.
requestAnimationFrame
这个函数与settimeout很相似,但它是专门为动画而生的.settimeout经常被用来做动画.我们知道动画达到60帧,用户就无法感知画面间隔.每一帧大约16毫秒.而requestAnimationFrame的帧率刚好是这个频率.除此之外相比于settimeout,还有以下的一些优点:
requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧,每帧大约16毫秒.
在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。
但它优于setTimeout/setInterval的地方在于它是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销。
总结:
浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:javascript引擎线程,GUI渲染线程,浏览器事件触发线程。
javascript引擎是基于事件驱动单线程执行的.JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。
当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。
推荐教程:《JS教程》
以上がjs setTimeOut() アプリケーションの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。