1. js首先是一门单线程语言,为什么呢?
这就和js的用途有关,js是作为浏览器的脚本语言,主要是实现用户与浏览器的交互,以及操作dom。这决定了它只能是单线程,否则会带来很复杂的同步问题。
比如同一个时间点,一个线程要修改一个dom元素,另一个线程要去删除这个dom元素,此时浏览器就不知道该怎么处理了,所以为了避免这种情况,设计之初就定为了单线程语言。
2. js任务分为同步任务和异步任务
同步任务:又叫做非耗时任务,指的是在主线程(即js引擎线程)上排队执行的那些任务,只有前一个任务执行完毕,才能执行后一个任务。
异步任务: 又叫做耗时任务,异步任务由JavaScript委托给浏览器对应线程执行(例如http请求线程、定时器触发线程、事件触发线程)进行执行。异步任务执行完之后会将异步任务的回调函数放入事件队列,等待主线程处理。
3. 异步任务又分为宏任务和微任务
宏任务主要包括: setTimeout / setInterval / postMessage(用于多窗口通信)
微任务主要包括: Promise.then / Object.observe(用于追踪对象的变化,已废除)
微任务的优先级要高于宏任务!
4. js执行过程
js运行时当遇到同步任务就会将其放入js执行栈中进行处理,入栈/出栈,按顺序执行完所有的同步任务,如果遇到异步任务就会将其交于对应的浏览器线程进行处理,处理完毕后会将其回调函数加入事件队列中等待主线程处理。需要注意的是,异步任务并不是在等待同步任务执行完后才会执行,异步任务其实已经交于其它线程进行执行了,执行完毕后将其回调函数放入事件队列,等待主线程处理完同步任务之后进行处理。因此可以说是异步任务的回调函数永远在同步任务之后执行,不能说时异步任务在回调函数之后执行。
事件队列细又分为宏任务队列和微任务队列,js执行每个宏任务前都会去查询微任务队列内是否有微任务,若果有,将执行完微任务队列之后再执行宏任务队列的任务。
5. 执行顺序总结
先同步后异步(实际是异步的回调) => 先微任务再宏任务 => 先同步后异步 => 先微任务再宏任务 => ....
6.事件循环机制
js的这种不断重复的进行读取任务、执行任务,再读取再执行的机制,就被称为事件循环机制。
- console.log(1) // 同1
-
- setTimeout(() => { // 宏1
- console.log(2)
- }, 0)
-
- const time = setInterval(() => { // 宏2
- console.log(3)
- }, 1000)
-
- setTimeout(() => { // 宏3
- clearInterval(time)
- console.log(4)
- }, 3000)
-
- new Promise((resolve) => {
- console.log(5) // 同2
- resolve()
- }).then(() => {
- console.log(6) // 微1
- }).then(() => {
- console.log(7) // 微2
- })
-
- console.log(8) // 同3
-
- // 1.区分同步和异步
- // 2.区分宏任务和微任务
- // 3.先同后异,先微后宏确定执行顺序
-
- // 1 => 5 => 8 => 6 => 7 => 2 => 3 => 3 => 3 => 4