赞
踩
操作系统是计算机系统的核心软件,进程和线程是其中的两个概念。
进程:是计算机已经运行的程序,可以认为启动一个应用程序,就会默认启动一个进程(也可以是多个进程);
线程:是进程中的一个执行单元,每一个进程中都会启动至少一个线程用来执行程序中的代码,这个线程被称为主线程。
操作系统、进程、线程可以用一个形象的例子理解:
操作系统类似一个工厂;
工厂里的车间就是进程;
每个车间至少一个工人在工作,这个工人就是线程;
1.关于js
js是单线程的,js的线程是在浏览器或者Node的进程中;就意味着,js的代码,在同一时刻只能执行一个任务;当执行耗时的操作时,它可能会阻塞主线程的执行,导致用户界面无响应,页面冻结或卡顿。为了解决这个问题,JavaScript引入了异步编程的概念,通过回调函数、Promise、async/await等机制来处理耗时操作。
异步编程允许将耗时的操作交给其他线程或进程来执行,主线程可以继续执行其他任务,从而避免阻塞。在Web浏览器中,常见的异步操作包括网络请求(如Ajax请求)、定时器(setTimeout、setInterval)、事件处理等。这些异步操作的回调函数等到时间到的时候会被添加到浏览器的事件队列(task queue)中,主线程会不断地从事件队列中取出任务并执行。
事件队列又分为宏任务队列(macrotask queue)和微任务队列
script
,ajax,UI rendering,setTimeout
,setImmdiate,setInterval等
process.nextTick,
async 函数await下面的代码,queueMicrotask等
浏览器的事件循环
浏览器的事件循环(event loop)是一种机制,用于管理和调度 JavaScript 代码在浏览器中的执行顺序。以确保 JavaScript 异步代码的执行不会阻塞主线程,并保持页面的响应性。
js线程中遇到耗时的操作,会将这些操作在浏览器的其它线程进行执行,完成相应的操作后将回调函数和对应的结果放到事件队列中,依次取出到主线程中执行,这三者之间形成一个环在相互配合操作。
事件循环的流程:
第一步:js
解释器识别所有 js
代码,将main script中的代码放到主线程执行;比较耗时的代码会交给浏览器的其他线程去处理,处理完了会对应的放到宏任务队列或者微任务队列里等js引擎取出放到主线程中执行。
第二步:每次开始新的宏任务执行前先执行所有的微任务。
之后一直循环第一步,第二步,也就是常说的Event Loop(事件循环)
事件循环可以用下面这张流程图理解
注意:
1.每执行完一个宏任务后 都会将微任务清空 然后再从宏任务队列中取出第一个宏任务执行
2.宏任务是到时间了才会放在宏任务队列
3.微任务是立刻放入到微任务队列中的
为了理解来分析一段较复杂的代码:
- console.log('1');
-
- setTimeout(function() {
- console.log('2');
- process.nextTick(function() {
- console.log('3');
- })
- new Promise(function(resolve) {
- console.log('4');
- resolve();
- }).then(function() {
- console.log('5')
- })
- })
- process.nextTick(function() {
- console.log('6');
- })
- new Promise(function(resolve) {
- console.log('7');
- resolve();
- }).then(function() {
- console.log('8')
- })
-
- setTimeout(function() {
- console.log('9');
- process.nextTick(function() {
- console.log('10');
- })
- new Promise(function(resolve) {
- console.log('11');
- resolve();
- }).then(function() {
- console.log('12')
- })
- })
-
-

第一轮事件循环流程分析如下:
整体script作为第一个宏任务进入主线程,遇到console.log,输出1
。
遇到setTimeout,其回调函数被分发到宏任务队列中。我们暂且记为setTimeout1
。
遇到process.nextTick(),其回调函数被分发到微任务队列中。我们记为process1
。
遇到Promise,new Promise直接执行,输出7。then被分发到微任务队列中。我们记为then1
。
又遇到了setTimeout,其回调函数被分发到宏任务队列中,我们记为setTimeout2
。
下表是第一轮事件循环宏任务结束时各Event Queue的情况,此时已经输出了1
和7
。
对照上述的事件循环流程图 宏任务结束之后我们接下来就开始去查看微任务中是否有任务 如果有就执行所有的微任务 这里有两个微任务process1和then1
6
。8
。好了,第一轮事件循环正式结束,这一轮的结果是输出1,7,6,8
。那么第二轮事件循环从setTimeout1宏任务开始:
2
。接下来遇到了process.nextTick(),同样将其分发到微任务队列中,记为process2。4
,then也分发到微任务队列中,记为then2第二轮事件循环宏任务执行结束,执行两个微任务process2和then2。
好了,第二轮事件循环正式结束,这二轮的结果是输出2,4,3,5
。那么第三轮事件循环从setTimeout2宏任务开始:
第三轮事件循环宏任务执行结束,执行两个微任务process3和then3。
第三轮事件循环结束,第三轮输出9,11,10,12
。
整段代码,共进行了三次事件循环,完整的输出为1,7,6,8,2,4,3,5,9,11,10,12
。
总结:下一个宏任务执行前会去查看微任务队列中是否有任务 有就执行所有的微任务 微任务全部执行完 再去执行下一个宏任务
在看一个列子:
- setTimeout(() => {
- console.log(1)
- }, 1000);
- setTimeout(() => {
- console.log(2)
- }, 500);
- new Promise((resolve,reject)=>{
- console.log(3)
- resolve()
- }).then(()=>{
- console.log(4)
- })
第一轮事件循环流程分析如下:
整体script作为第一个宏任务进入主线程,
遇到setTimeout,其回调函数在1000s后被分发到宏任务队列中。我们暂且记为setTimeout1
。
遇到setTimeout,其回调函数在500s被分发到宏任务队列中。我们暂且记为setTimeout2
。
new Promise立即执行输出3,then会立刻分发到微任务队列中,记为then
第一轮事件循环宏任务执行结束,有一个微任务then,执行得到结果4
第二轮事件循环从setTimeout2宏任务开始:
输出2,没有微任务
第三轮事件循环从setTimeout1宏任务开始:
输出1,也没有微任务
所以最后输出3,4,2,1
总结:
宏任务并不是立马放入宏任务队列的而是等到时间到了之后在放入
微任务会立马放入微任务队列中
另外补充一个知识点,因为async、await是Promise的语法糖,所以await关键字后面执行的代码可以看成是放在(resolve,reject) => {}中执行的代码,await下一条语句可以看出是在then(res=> {})中执行的
下面出两个题供大家练习
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。