Home > Article > Web Front-end > Browser UI multi-threading and understanding of JavaScript single-threaded underlying operating mechanism
As early as when I first learned JavaScript, I was "indoctrinated" with this idea
JavaScript is single-threaded
But as I continued to During the learning process
I learned the asynchronous loading of timers and ajax
It once made me doubt this sentence
Since JavaScript is single-threaded, why does it still have asynchronous loading?
Later I learned that there is not only a js thread in the browser, but it together with other threads - Browser UI multi-threading
I have long wanted to write an article like this I just feel like I don’t understand enough, and I’m afraid that what I wrote may mislead everyone
After reading all the blog posts written by everyone online, I felt even more confused
But in the end I mustered up the courage and decided to talk about it ╮(╯_╰)╭
Let’s put aside the issue of JavaScript threads now, let’s first take a look at what is a browser UI thread
First Come to the previous picture
Generally, browsers have at least three resident threads
GUI rendering thread(Rendering page)
JS engine thread(Processing script)
Event trigger thread (Control interaction)
Including browsers sometimes open new threads, such as Http request threads (Ajax) that are thrown away after use, and other threads
They are composed together The UI thread of the browser
These threads work in an orderly manner under the control of the UI thread
There are inconsistent opinions on this resident thread on the Internet, and the implementation of each browser may be different, so I will not go into it here.
Although I put the js engine thread in the lower right corner, it is the main thread of the browser
And it is incompatible with the GUI rendering thread and cannot work at the same time
The reason is simple , because they all have to operate the DOM. If the js thread wants a certain DOM style, the rendering engine must stop working (the overbearing president asks you to stand there and don't move)
Why JavaScript is single-threaded
Single-threaded means that it can only do one thing at the same time
So is multi-threading in JavaScript bad? How efficient is it?
Not good
js is designed to interact with users. Processing DOM
If JavaScript is multi-threaded, then it must deal with the problem of multi-thread synchronization (I vaguely remember the horror of being dominated by C++ thread synchronization)
If js is multi-threaded, and one thread wants to modify the DOM at the same time, When another thread wants to delete the DOM
, the problem becomes much more complicated. The browser doesn't know who to listen to. If a "lock" mechanism is introduced, it will be very troublesome (then I won't learn the front-end ( ̄_,  ̄ ))
So there is no need for a scripting language like ours to be so complicated, so JavaScript has been single-threaded since its birth
Although H5 proposed Web Worker, it cannot operate the DOM, so it still needs to be Entrust a big brother js main thread to solve the problem
I don’t know much about this Web Worker, so I won’t say more here
These sub-threads are completely controlled by the main thread big brother, so they do not change the single-threaded nature of JavaScript
Let’s first take a look at what is the execution stack
The stack is a first-in-last-out (FILO) data structure
The execution stack stores the tasks being executed, each task Called "frame"
For example
function foo(c){ var a = 1; bar(200); }function bar(d){ var b = 2; } foo(100);
Let's take a look at how the execution stack has changed
At the beginning, when the code is not executed, the execution The stack is empty.
#When the foo function is executed, a frame is created. This frame contains formal parameters and local variables (precompilation process), and then this frame is pushed onto the stack. In
then execute the code in the foo function, execute the bar function
to create a new frame, which also has formal parameters and local variables, and is pushed onto the stack
The bar function is executed and the stack is popped
The foo function is executed and the stack is popped
The execution stack is empty
The execution stack is actually equivalent to the js main thread
The queue is a first-in-first-out (FIFO) data structure
There is also a task queue in the js thread
The task queue contains a series of tasks to be processed
Single thread means that all tasks need to be executed one after another. If a task takes too long to execute, Then you have to wait for the subsequent tasks
It's like a nurse giving an injection to the children in line. If the child at the front keeps rolling the needle, it will keep getting injected, and the children behind will have to wait (this metaphor seems inappropriate)
But if the child at the front faints from the needle
then the nurse aunt cannot sit there. When he wakes up, he must give the child at the back the needle first
This is equivalent to "hanging up" the child "Start" (asynchronous)
So, tasks can be divided into two types
Synchronous tasks
Asynchronous tasks
同步任务就是正在主线程执行栈中执行的任务(在屋子内打针的小朋友)
而异步任务是在任务队列等候处理的任务(在屋子外等候打针的小朋友)
一旦执行栈中没有任务了,它就会从执行队列中获取任务执行
任务队列是一个事件的队列,IO设备(输入/输出设备)每完成一项任务,就会在任务队列中添加事件处理
用户触发了事件,也同样会将回调添加到任务队列中去
主线程执行异步任务,便是执行回调函数(事件处理函数)
只要执行栈一空,排在执行队列前面的会被优先读取执行,
不过主线程会检查时间,某些事件需要到了规定时间才能进入主线程处理(定时器事件)
主线程从执行队列不断地获取任务,这个过程是循环不断地,叫做“Event Loop”事件循环
同步任务总是会在异步任务之前执行
只有当前的脚本执行完,才能够去拿任务队列中的任务执行
前面也说到了,任务队列中的事件可以是定时器事件
定时器分为两种 setTimeout() 和 setInterval()
前者是定时执行一次,后者定时重复执行
第一个参数为执行的回调函数,第二个参数是间隔时间(ms)
来看这样一个例子
setTimeout(function(){ console.log('timer'); },1000);console.log(1);console.log(2);console.log(3);
这个没什么问题,浏览器打印的是 1 2 3 timer
但是这样呢
setTimeout(function(){ console.log('timer'); },0);//0延时console.log(1); console.log(2); console.log(3);
浏览器打印依然打印的是 1 2 3 timer
也许有同学知道,旧版浏览器,setTimeout定时至少是10ms(即便你设置了0ms),
H5新规范是定时至少4ms(我读书少不知道为什么),改变DOM也是至少16ms
也许这是因为这个原因
那么我再改动一下代码
setTimeout(function(){ console.log('timer'); },0);var a = +new Date();for(var i = 0; i < 1e5; i++){ console.log(1); }var b = +new Date(); console.log(b - a);
这回够刺激了吧,输出10w次,我浏览器都假死了(心疼我chrome)
不仅如此,我还打印了循环所用时间
来看看控制台
输出了10w个1,用了将近7s
timer依然是最后打印的
这就证明了我前面说的话: 同步任务总是会在异步任务之前执行
只有我执行栈空了,才会去你任务队列中取任务执行
最后我举一个例子加深一下理解
demo.onclick = function(){ console.log('click'); }function foo(a){ var b = 1; bar(200); }function bar(c){ var d = 2; click//伪代码 此时触发了click事件(这里我假装程序运行到这里手动点击了demo) setTimeout(function(){ console.log('timer'); }, 0); } foo(100);
怕大家蒙我就不写Ajax了
Ajax如果处理结束后(通过Http请求线程),也会将回调函数放在任务队列中
还有一点click那一行伪代码我最开始是想用demo.click()模拟触发事件
后来在测试过程中,发现它好像跟真实触发事件不太一样
它应该是不通过触发事件线程,而是存在于执行栈中,就相当于单纯地执行click回调函数
不过这只是我自己的想法有待考证,不过这不是重点,重点是我们理解这个过程,请大家不要吐槽我╰( ̄▽ ̄)╭
下面看看执行这段代码时发生了什么(主要说栈和队列的问题,不会赘述预编译过程)
主线程开始执行,产生了栈、堆、队列
demo节点绑定了事件click,交给事件触发线程异步监听
执行foo函数(之前同样有预编译过程),创建了帧包括foo函数的形参、局部变量压入执行栈中
foo函数内执行bar函数,创建帧包括bar函数的形参、局部变量压入执行栈中
触发了click事件,事件触发线程将回调事件处理函数放到js线程的任务队列中
触发了定时器事件,事件触发线程立即(4ms)将回调处理函数放到js线程的任务队列中
bar函数执行完毕,弹出栈
foo函数执行完毕,弹出栈
此时执行栈为空
执行栈向任务队列中获取一个任务:click回调函数,输出‘click’
执行栈项任务队列中获取一个任务:定时器回调函数,输出‘timer’
执行结束
这里从任务队列里不断取任务的过程就是Event Loop
有一些我的理解,如果发现不对或者有疑问的地方,请联系我
相信大家看了这个例子应该对js底层运行机制有了一个大概的了解
以上就是Browser UI multi-threading and understanding of JavaScript single-threaded underlying operating mechanism及对JavaScript单线程底层运行机制的理解的内容,更多相关内容请关注PHP中文网(www.php.cn)!