setTimeout 時間が 0 に設定される 詳細な分析

亚连
リリース: 2018-05-30 15:56:35
オリジナル
1762 人が閲覧しました

setTimeout() は window に属するメソッドですが、これは時間を設定するために使用されます。主に次の記事で説明します。 setTimeout 時間を 0 に設定することに関する関連情報を紹介します。必要な友人は参照してください。

はじめに

この記事では主に setTimeout 時間を 0 に設定することに関する関連内容を紹介し、参考と学習のために共有します。以下では多くのことを説明しません。

1. 前菜、setTimeoutとは何ですか

まず、上記のw3schoolのsetTimeoutの説明を見てみましょう

setTimeout(fn, millisec)メソッドは、指定された関数を呼び出したり、式を計算したりするために使用されますミリ秒数。

それは非常に単純です。 setTimeout() は fn を 1 回だけ実行します。実行されるタイミングは 2 番目のパラメータ millisec で設定されたミリ秒数に依存するため、多くの人はこれを遅延と呼ぶことに慣れています。しばらくしてから内部のコードを実行するだけです。 setTimeout() 只执行 fn 一次,到底什么时候执行取决于第二个参数millisec设定的毫秒数,所以很多人习惯上称之为延迟,无非就是延迟一段时间后再执行里面的代码。

setTimeout(function(){
 console.log('我是setTimeout');
}, 1000);
ログイン後にコピー

正常情况下,我是setTimeout 这句话并不会马上输出而是等1000毫秒以后会在浏览器的控制台输出。

2、主菜,js是单线程的

OK,看一个例子,这个例子的输出结果是什么?

setTimeout(function(){
 console.log(1);
}, 0);
console.log(2);
console.log(3);
ログイン後にコピー

出乎一些人的意料,得到的结果竟然是2、3、1。这似乎不按套路出牌啊,明明是等待了0毫秒也就是不等待直接输出啊,为啥1却在最后输出了呢?

这就需要搞清楚一个很重要的概念:js是单线程的,单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。

其实很好理解,就像大家去超市买东西一样,所有买东西的人都需要在收银台排队结账,正常情况下每个收银台同一时间只能为一位顾客结账,这位顾客结账完成才能为下一位顾客服务。

而浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:javascript引擎线程,GUI渲染线程,浏览器事件触发线程。

  • javascript引擎是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。

  • GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

  • 事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。(当线程中没有执行任何同步代码的前提下才会执行异步代码)

其实,当js代码执行遇到setTimeout(fn,millisec)时,会把fn这个函数放在任务队列中,当js引擎线程空闲时并达到millisec指定的时间时,才会把fn放到js引擎线程中执行。

setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。

HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常不会立即执行,而是每16毫秒执行一次。这时使用requestAnimationFrame()的效果要好于setTimeout()

需要注意的是,setTimeout()只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()

var debounce = function(method, context) {
 clearTimeout(method.tId);
 method.tId = setTimeout(function(){
  method.call(context);
 },100);
}
ログイン後にコピー
ログイン後にコピー

通常の状況では、この文はすぐに出力されず、1000 ミリ秒後にブラウザーのコンソールに出力されます。

2. メインコース、js はシングルスレッドです🎜🎜🎜🎜🎜 それでは、例を見てみましょう。 🎜

🎜

setTimeout(function () {
 console.log('我被调用了');
 setTimeout(arguments.callee, 100);
}, 100);
ログイン後にコピー
ログイン後にコピー
🎜🎜🎜 驚いたことに、結果は 2、3、1 でした。これはルーチンに従っていないようです。明らかに 0 ミリ秒待機します。つまり、待機せずに直接出力します。なぜ最後に 1 が出力されるのでしょうか。 🎜🎜これには非常に重要な概念を明確にする必要があります。js はシングルスレッドです。シングルスレッドとは、すべてのタスクをキューに入れる必要があり、前のタスクが完了するまで次のタスクは実行されないことを意味します。前のタスクに時間がかかると、次のタスクも待たされることになります。 🎜🎜🎜実際、誰もがスーパーに物を買いに行くのと同じように、通常の状況では、各レジで会計できるのは 1 人の顧客だけです。この顧客はチェックアウトが完了するまで次の顧客にサービスを提供できません。 🎜🎜ブラウザのカーネルはマルチスレッドであり、カーネルの制御の下で相互に連携して、ブラウザには少なくとも 3 つの常駐スレッド (JavaScript エンジン スレッド、GUI レンダリング スレッド、ブラウザ イベント トリガー スレッド) が実装されています。 🎜
  • 🎜 JavaScript エンジンはイベント駆動型のシングルスレッド実行に基づいており、JS エンジンはタスク キュー内のタスクの到着を待機してから処理します。ブラウザは、JS プログラムを実行する JS スレッドは 1 つだけです。 🎜🎜
  • 🎜 GUI レンダリング スレッドは、ブラウザ インターフェイスのレンダリングを担当します。インターフェイスを再描画する必要がある場合 (Repaint)、または何らかの操作によってリフローが発生した場合、このスレッドが実行されます。ただし、GUI レンダリング スレッドと JS エンジンは相互に排他的であることに注意してください。JS エンジンが実行されると、GUI スレッドは一時停止され、GUI の更新はキューに保存され、JS エンジンが実行されるとすぐに実行されます。アイドル。 🎜🎜
  • 🎜イベントトリガースレッド。イベントがトリガーされると、スレッドは保留キューの最後にイベントを追加し、JS エンジンによる処理を待ちます。これらのイベントは、setTimeOut などの JavaScript エンジンによって現在実行されているコード ブロック、またはマウス クリック、AJAX 非同期リクエストなどのブラウザ カーネル内の他のスレッドから発生する可能性があります。ただし、JS のシングルスレッド関係により、すべてのイベントが発生します。これらのイベントは、JS エンジンによる処理のためにキューに入れられる必要があります。 (スレッド内で同期コードが実行されていない場合、非同期コードが実行されます) 🎜🎜
🎜実際、js コードの実行で setTimeout(fn, millisec) が発生したとき、関数 fn はタスク キューに配置され、js エンジン スレッドがアイドル状態になり、millisec で指定された時間に達すると、fn は実行のために js エンジン スレッドに配置されます。 🎜🎜setTimeout(fn,0) は、メインスレッドの利用可能な最も早いアイドル時間に実行されるタスク、つまり、できるだけ早く実行されるタスクを指定することを意味します。 「タスクキュー」の最後にイベントを追加するため、同期タスクと「タスクキュー」内の既存のイベントが処理されるまで実行されません。 🎜🎜HTML5 標準では、setTimeout() の第 2 パラメータの最小値 (最短間隔) が 4 ミリ秒未満であってはならないと規定されており、この値より小さい場合は自動的に増加します。これより前の古いブラウザでは、最小間隔が 10 ミリ秒に設定されていました。さらに、これらの DOM 変更 (特にページの再レンダリングを伴う変更) は通常、すぐには実行されず、16 ミリ秒ごとに実行されます。現時点では、setTimeout() よりも requestAnimationFrame() を使用した方が効果が高くなります。 🎜🎜 setTimeout() はイベントを「タスク キュー」に挿入するだけであることに注意してください。メインスレッドは、現在のコード (実行スタック) の実行が完了するまで待機してから、タスクを実行する必要があります。コールバック関数を指定します。現在のコードに時間がかかる場合は、長時間かかる可能性があるため、setTimeout() で指定された時間にコールバック関数が実行されることを保証する方法はありません。 🎜🎜🎜🎜3. デザート、setTimeout の素晴らしい使い方🎜🎜🎜🎜

其实setTimeout是有一些妙用的,这里简单列举几个。

函数去抖

让一个函数在一定间隔内没有被调用时,才开始执行被调用方法。比如当你在使用 google 搜索内容的时候,有些关键词输入到一半,谷歌会展示一个可选列表,根据你当前输入的内容作出的一个猜测联想。需要监听文字改变,每一次改变都会调用一次回调函数,现在需要的一种实现是在用户停止键盘事件一段时间后,去发送一个请求。

var debounce = function(method, context) {
 clearTimeout(method.tId);
 method.tId = setTimeout(function(){
  method.call(context);
 },100);
}
ログイン後にコピー
ログイン後にコピー

轮训任务

js中可以使用setInterval开启轮询,但是这种存在一个问题就是执行间隔往往就不是你希望的间隔时间。

比如有个轮询任务间隔是100ms,但是执行方法的时间需要450ms,那么在200ms、300ms、400ms本来是计划中执行任务的时间,浏览器发现第一个还未执行完,那么就会放弃2、3、4次的任务执行,并且在500ms之后再次执行任务,这样的话,其实再次执行的间隔就只有50ms。使用setTimeout构造轮询能保证每次轮询的间隔。

setTimeout(function () {
 console.log('我被调用了');
 setTimeout(arguments.callee, 100);
}, 100);
ログイン後にコピー
ログイン後にコピー

延迟js引擎的调用

var p = document.createElement('p');
p.innerHTML = '我是一个p';
p.setAttribute('style', 'width:200px; height:200px;background-color:#f00; ');
document.body.appendChild(p);
setTimeout(function() {
 console.log('我被调用了');
}, 1000);
ログイン後にコピー

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

jQuery替换节点元素的操作方法

Vue中computed与methods的区别详解

javaScript实现鼠标在文字上悬浮时弹出悬浮层效果

以上がsetTimeout 時間が 0 に設定される 詳細な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート