setTimeout, a function that front-end engineers must deal with. It looks very simple and simple. It has a very extraordinary name - timer. When I was young, I naively thought that I could control the future. But I didn't know how simple it is. There is a shocking secret hidden in it. I still remember the first time I used this function, I naively thought it was a tool for realizing multi-threading in js. At that time, I used it to implement a small game about tank battles, and it was hard to play. It's a pleasure. But as I go further and further on the front-end road, my understanding of it begins to change. It seems to be covered with a veil, and there are often some strange behaviors that I can't figure out. Finally, my patience Exhausted, I made up my mind to tear off its mask and find out.
To talk about the origin of setTimeout, we have to start with its official definition. This is how w3c defines it
The setTimeout() method is used to call a function or calculated expression after a specified number of milliseconds.
Seeing such a description, we understand that it is a timer, and the function we set is an "alarm clock", which will be executed when the time is up. However, if you are smart, you can't help but think like this A question, what if it is settimeout(fn,0)? According to the definition, will it be executed immediately? Practice is the only criterion for testing truth. Let us take a look at the following experiment
<!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>
This is a A very simple experiment, if settimeout(0) will be executed immediately, then the execution result here should be 1->2>3. However, the actual result is 1->3->2. This shows that settimeout (0) is not executed immediately. At the same time, we feel very strange about the behavior of settimeout.
The js engine is executed in a single thread
Let’s first address the above issues Let’s put it aside and see if we can find any clues from the design of the js language.
We found that a very important point in the design of the js language is that js does not have multi-threading. The execution of the js engine is single-threaded Execution. This feature has troubled me for a long time. I couldn't figure out that since js is single-threaded, who will time the timer? Who will send the ajax request? I fell into a blind spot. That is to say, js is equivalent to the browser .We are used to executing code in the browser, but ignore the browser itself. The js engine is single-threaded, but the browser can be multi-threaded. The js engine is just a thread of the browser. Timer timing, network Requests, browser rendering, etc. are all completed by different threads. Without any proof, let's still look at an example
<!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 is true by default, and it is an infinite loop in while. Finally The alert will not be executed. I added a timer and changed isEnd to false after 1 second. If the js engine is multi-threaded, then the alert will be executed after 1 second. However, the actual situation is, The page will loop forever. alert is not executed. This is a good proof that settimeout cannot be used as a multi-thread. The js engine execution is single-threaded.
event loop
From the above experiment, we are even more confused, what exactly does settimeout do?
It turns out that we still have to find the answer from the design of the js language.
The js engine is executed in a single thread. It is based on an event-driven language. Its execution sequence follows a mechanism called event queue. As we can see from the figure, browsers have various Threads, such as event triggers, network requests, timers, etc. The connections between threads are all based on events. When the js engine processes the code related to other threads, it will be distributed to other threads. After they are processed, js is needed When the engine calculates, it adds a task to the event queue. During this process, js does not block the code and waits for other threads to complete execution, and after other threads complete execution, the event task is added to tell the js engine to perform relevant operations. This is the asynchronous programming of js Model.
So when we look back at settimeout(0), we will suddenly realize that when the js code is executed here, a timer thread will be started, and then the following code will continue to be executed. The thread will be executed at the specified time Then insert a task into the event queue. It can be seen that the operations in settimeout(0) will be placed after all main thread tasks. This also explains why the first experimental result is 1->3-2.
It can be seen that the official definition of settimeout is confusing. A new definition should be given:
Within the specified time, put the task into the event Queue, waiting for the js engine to be executed after it is idle.
The js engine and the GUI engine are mutually exclusive
Speaking of this, we have to Let’s talk about another engine of the browser---GUI rendering engine. The rendering operation in js is also asynchronous. For example, the DOM operation code will generate a task in the event queue, and when js executes this task, it will call the GUI engine rendering .
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教程》
The above is the detailed content of js setTimeOut() application. For more information, please follow other related articles on the PHP Chinese website!