Timer
setTimeout
は、指定したミリ秒後に関数を呼び出したり、式を計算したりするためによく使用されます。
構文:
setTimeout(code, millisec, args);
注: コードが文字列の場合、コードを実行するために
eval()
メソッドを実行するのと同じです。
もちろん、この記事では
setTimeout
の使用方法を説明するだけでなく、それがどのように実行されるかも理解します。
の原理 まずコードの一部を見てみましょう:
var start = new Date(); var end = 0; setTimeout(function() { console.log(new Date() - start); }, 500); while (new Date() - start <= 1000) {}
上記のコードでは、
setTimeout
タイマーが定義されており、遅延時間は 500 ミリ秒です。
印刷結果は500だと思いますか
しかし、事実はあなたの予想を超えており、印刷結果は次のようになります(印刷すると異なるかもしれませんが、間違いなく1000ミリ秒を超えるでしょう):
どうしてですか?その理由は、JavaScript が単一スレッドで実行されるためです。つまり、どの時点においても、JavaScript プログラムを実行するスレッドは 1 つだけであり、複数のコードを同時に実行することはできません。
ブラウザーでの JavaScript を見てみましょう。
ブラウザのカーネルはマルチスレッドであり、カーネルの制御下で相互に連携して、ブラウザには少なくとも 3 つの常駐スレッド (JavaScript エンジン スレッド、GUI レンダリング スレッド、ブラウザ イベント トリガー スレッド) が実装されています。
JavaScript引擎
は、イベント駆動型のシングルスレッド実行に基づいています。JavaScript エンジンは、タスク キュー内のタスクの到着を待ってから、それらを処理します。ブラウザーでは、常に 1 つの JavaScript スレッドのみが JavaScript プログラムを実行します。 。
GUI渲染线程
は、ブラウザインターフェースのレンダリングを担当します。インターフェースの再描画(Repaint)が必要な場合、または何らかの操作によってリフロー(Reflow)が発生した場合、このスレッドが実行されます。ただし、GUI レンダリング スレッドと JavaScript エンジンは相互に排他的であることに注意してください。JavaScript エンジンが実行されると、GUI スレッドは一時停止され、GUI の更新はキューに保存され、JavaScript が実行されるとすぐに実行されます。エンジンはアイドル状態です。
事件触发线程
、イベントがトリガーされると、スレッドはそのイベントを保留キューの最後に追加し、JavaScript エンジンによる処理を待ちます。これらのイベントは、setTimeout などの JavaScript エンジンによって現在実行されているコード ブロックから、またはマウス クリックや Ajax 非同期リクエストなどのブラウザ カーネル内の他のスレッドから発生する可能性があります。ただし、JavaScript のシングルスレッド関係により、すべてのイベントが発生します。これらのイベントはキューに入れて、JavaScript エンジンによる処理を待つ必要があります (スレッド内で同期コードが実行されていない場合にのみ、非同期コードが実行されます)。
ここで、元の例を確認してみましょう:
var start = new Date(); var end = 0; setTimeout(function() { console.log(new Date() - start); }, 500); while (new Date() - start <= 1000) {}
setTimeout
の遅延時間は500ミリ秒ですが、
while
ループの存在により、間隔が1000ミリ秒を超える場合にのみ、 while ループから抜け出します。つまり、1000 ミリ秒より前に、while ループが JavaScript スレッドを占有していました。つまり、while から抜け出すのを待った後にのみ、スレッドはアイドル状態になり、以前に定義された setTimeout を実行します。
最後に、
setTimeout
はタスク(実行する必要のある関数)が指定された時間後にタスクキューに挿入されて待機することを保証するだけであり、タスクがいつ実行されるかは保証しないと結論付けることができます。 。 JavaScript を実行しているスレッドが空になったら、タスクをキューから削除して実行します。
JavaScript スレッドは時間のかかる操作によってブロックされないため、キュー内のタスクをすぐに取り出して実行できます。また、このキュー メカニズムが非同期実行の錯覚を生み出します。
おそらく次のコードを見たことがあるでしょう:
setTimeout(function(){ // statement}, 0);
上記のコードは即時実行を意味します。本来の目的は、呼び出した関数をすぐに実行することですが、実際には、setTimeout には最小実行時間が設定されており、指定された時間がその時間より短い場合、ブラウザーは許容される最小実行時間を使用するためです。つまり、setTimeout の遅延時間を 0 に設定しても、呼び出されたプログラムはすぐには開始されません。
ブラウザごとに実際の状況は異なります。IE8以前の時間精度は15.6msです。ただし、HTML5 の登場により、ブラウザの高度なバージョン (Chrome、IE9+ など) では、定義された最小時間間隔は 4 ミリ秒以上となり、この値よりも低い場合は自動的に増加します。2010 年には。その後、公開されたブラウザ全体で一貫して採用されました。
そのため、
setTimeout(fn,0)
として記述すると、実際にはキュージャンプ操作を実装し、ブラウザに「できるだけ早く」コールバックを実行するよう要求しますが、実際にどれくらい速くできるかはブラウザに完全に依存します。 。
では、
setTimeout(fn, 0)
は何の役に立つのでしょうか?実際、タスクの実行順序を変更できるという利点があります。ブラウザは現在のタスク キュー内のタスクを完了した後、setTimeout キューに蓄積されたタスクを実行するためです。
遅延後に実行するタスクを0sに設定することで、タスクの実行順序を変更したり、タスクの発生を遅らせたり、非同期で実行させることができます。
インターネットでよくある例を見てみましょう:
document.querySelector('#one input').onkeydown = function() { document.querySelector('#one span').innerHTML = this.value; }; document.querySelector('#second input').onkeydown = function() { setTimeout(function() { document.querySelector('#second span').innerHTML = document.querySelector('#second input').value; }, 0); };
`实例:实例
当你往两个表单输入内容时,你会发现未使用setTimeout函数的只会获取到输入前的内容,而使用setTimeout函数的则会获取到输入的内容。
这是为什么呢?
因为当按下按键的时候,JavaScript 引擎需要执行 keydown 的事件处理程序,然后更新文本框的 value 值,这两个任务也需要按顺序来,事件处理程序执行时,更新 value值(是在keypress后)的任务则进入队列等待,所以我们在 keydown 的事件处理程序里是无法得到更新后的value的,而利用 setTimeout(fn, 0),我们把取 value 的操作放入队列,放在更新 value 值以后,这样便可获取出文本框的值。
未使用setTimeout函数,执行顺序是:`onkeydown => onkeypress => onkeyup
使用setTimeout函数,执行顺序是:
onkeydown => onkeypress => function => onkeyup`
虽然我们可以使用
keyup
来替代
keydown
,不过有一些问题,那就是长按时,
keyup
并不会触发。
长按时,keydown、keypress、keyup的调用顺序:
keydown keypress keydown keypress ... keyup
也就是说keyup只会触发一次,所以你无法用keyup来实时获取值。
我们还可以用
setImmediate()
来替代
setTimeout(fn,0)
:
if (!window.setImmediate) { window.setImmediate = function(func, args){ return window.setTimeout(func, 0, args); }; window.clearImmediate = window.clearTimeout; }
setImmediate()`方法用来把一些需要长时间运行的操作放在一个回调函数里,在浏览器完成后面的其他语句后,就立刻执行这个回调函数,必选的第一个参数func,表示将要执行的回调函数,它并不需要时间参数。
注意:目前只有IE10支持此方法,当然,在Nodejs中也可以调用此方法。
3.1 setTimeout中回调函数的this
由于setTimeout() 方法是浏览器 window 对象提供的,因此第一个参数函数中的this其实是指向window对象,这跟变量的作用域有关。
看个例子:
var a = 1; var obj = { a: 2, test: function() { setTimeout(function(){ console.log(this.a); }, 0); } }; obj.test(); // 1
不过我们可以通过使用bind()方法来改变setTimeout回调函数里的this
var a = 1; var obj = { a: 2, test: function() { setTimeout(function(){ console.log(this.a); }.bind(this), 0); } }; obj.test(); // 2
3.2 setTimeout不止两个参数
我们都知道,setTimeout的第一个参数是要执行的回调函数,第二个参数是延迟时间(如果省略,会由浏览器自动设置。在IE,FireFox中,第一次配可能给个很大的数字,100ms上下,往后会缩小到最小时间间隔,Safari,chrome,opera则多为10ms上下。)
其实,setTimeout可以传入第三个参数、第四个参数….,它们表示神马呢?其实是用来表示第一个参数(回调函数)传入的参数。
setTimeout(function(a, b){ console.log(a); // 3 console.log(b); // 4},0, 3, 4);
以上就是JavaScript 开发者应该知道的 setTimeout 秘密 的内容,更多相关内容请关注PHP中文网(www.php.cn)!