事件循环(eventloop)
同步和异步
JS是单线程的,也就是说, 同一时间只能做一件事,所有任务需要排队,前一个任务结束之后才会执行下一个任务。
作为浏览器脚本语言,JavaScript的主要用途是和用户互动以及操作DOM,这决定了它只能是单线程
但我们知道,有些任务是耗时的,会阻塞代码的执行。
为了解决这个问题,我们引出了同步和异步的概念。
可以把代码分为同步代码(同步任务)和异步代码(异步代码)。
异步代码(耗时) | 同步代码(立即执行) |
---|---|
先放入宿主环境(浏览器/Node) ,不必原地等待结果。(并不阻塞主线程继续往下执行,异步结果在将来执行) | 立即放入JS 引擎(JS主线程) 执行,并原地等待结果 |
setTimeout(一次性定时器) | 如console.log(1) |
setInterval(定时器) | … |
Ajax/Fetch 事件绑定 | … |
Promise里的then、catch … | … |
执行过程(执行栈、宿主环境、任务队列):
- 同步代码放入执行栈中,异步代码放在宿主环境,等待时机成熟送入任务队列( 如绑定的事件被触发或定时器时间到了,对应回调函数送到任务队列)
- 执行栈中代码执行完毕,会去任务队列看是否有异步任务,有就送到执行栈执行,反复循环查看执行,这个过程是事件循环(eventloop)
宏任务与微任务
概述
任务分为同步任务和异步任务,异步任务又可以分为宏任务和微任务。
在ES5之后,JavaScript引入了Promise,意味着不需要浏览器,JavaScript引擎自身也能够发起异步任务了。所以,JS可以执行同步和异步任务。
宏任务由宿主(浏览器、Node) 发起
,微任务由JS 引擎发起
宏任务 | 微任务 |
---|---|
script(代码块) | Promise的then/catch |
事件 | |
网络请求(Ajax/Fetch) | |
setTimeout() 一次性定时器/setlnterva() 定时器 |
注意:Promise本身是同步的,只有里面then/catch的回调函数是异步的
到现在我们可以把代码整体分为三类:
宏任务的异步代码(宿主环境) | 微任务的异步代码(js引擎) | 同步代码(js 执行栈/回调栈) |
---|---|---|
script(代码块) | Promise.then() / catch() | |
事件 | Async/Await | |
网络请求(Ajax/Fetch) | process.nextTick (node) | |
setTimeout() 一次性定时器/setlnterva() 定时器 | Object.observe 等等 | |
setlmmediate 定时器 | Object.observe | |
I/O | MutationObserver | |
Ul render |
宏任务、微任务执行过程及顺序
- 首先执行放在执行栈中的同步代码
- 把微任务里的异步代码放在微任务队列排队,根据先进先出原则依次执行
- 把宏任务里的异步代码放在宏任务队列排队,根据先进先出原则依次执行
具体描述:
执行执行栈中所有同步代码,执行完就去微任务队列里面看有没有微任务在进行排队,如果有的话,就将微任务通过事件循环的方式,根据先进先出原则依次推到执行栈中执行。一个微任务执行完之后再回到微任务队列里看还有没有在排队的,有的话就再推到执行栈中执行。所有微任务执行完毕之后,再去看看宏任务队列,重复像刚才的执行过程(看有没有排队的,有的话就按先进先出通过事件循环推到执行栈中执行),宏任务队列中所有任务执行完毕之后,整个代码就执行完毕了。
pink老师:微任务是由JS引擎发起的,离了我们JS更近,所以说会优先执行
有时候看到说是宏任务优先:
是因为script本身就是一个大的宏任务,我们所有的代码的操作其实是在一个大的宏任务里面去执行的
忽略script:同步任务 --> 微任务 --> 宏任务
每当主线程执行完一个宏任务后,不会立即执行下一个宏任务,而是接着先检查微任务队列。
如果微任务队列中存在待执行的任务,主线程会立即执行微任务,直到微任务队列为空。
因为微任务通常包含那些需要在当前逻辑上下文结束后、但在渲染或执行新的宏任务之前必须完成的操作。
总结
事件循环(eventloop):
不断地检查调用栈是否为空,并且如果任务队列中有待处理的任务,就将它们推入调用栈(执行栈)执行。
-
JS是单线程,防止代码阻塞,我们把代码(任务):同步和异步
-
同步代码给js引擎执行,异步代码交给宿主环境
-
同步代码放入执行栈中,异步代码等待时机成熟送入任务队列排队
-
执行栈执行完毕,会去任务队列看是否有异步任务,有就送到执行栈执行,反复循环查看执行,这个过程是事件循环(eventloop)
宏任务与微任务
宏任务:
- 宏任务被添加到宏任务队列中。
- 事件循环在每个迭代开始时,会检查调用栈是否为空。
- 如果调用栈为空,事件循环从宏任务队列中取出一个任务,推入调用栈执行。
- 浏览器在宏任务执行后会进行渲染更新。
微任务:
- 微任务被添加到微任务队列中。
- 事件循环在每个迭代中,一旦调用栈清空,就会立即执行微任务队列中的所有微任务。
- 微任务的执行顺序是先进先出,它们会在当前宏任务完成后、下一个宏任务开始前清空队列。
- 微任务的优先级高于宏任务,这意味着在执行下一个宏任务之前,所有的微任务都会被处理完毕。