这里主要记录在日常中对知识的学习,通过结合笔记与自身理解的方式尝试写下总结
文章对细节可能不会一一介绍解释,内容仅作参考
复制代码
一、背景
- JavaScript是一门单线程语言
- 单线程所带来的任务执行方式
二、同步任务与异步任务
单线程就意味着,所有的任务都需要排队,当前一个任务结束时,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等待。 如果是因为计算量大,CPU忙不过来倒也正常,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。于是,所有任务可以分成两种:
- 同步任务
- 异步任务
在这里用一张图来说明:
- 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table
- 当异步任务完成时,Event Table会将对应的回调移入Event Queue中
- JS引擎会持续不断检查主线程执行栈是否为空,当主线程内的任务全部执行完毕后,会去Event Queue读取排头第一个,进入到主线程执行
- 上述过程不断重复,形成事件循环(Event Loop)
三、宏任务与微任务
除了广义的同步任务和异步任务,会对任务有更精细的定义:
- 宏任务:整体代码、setTimeout、setInterval
- 微任务:Promise
不同类型的任务会进入对应的Event Queue,比如setTimeout和setInterval会进入相同的Event Queue
第一次进入整体代码(宏任务)后,开始第一遍循环,在主线程任务全部执行完毕后,会先去读取所有的微任务进行执行,然后再到宏任务,而宏任务里面或许又包含着宏任务与微任务。以此不断循环执行
用一张图说明:
举个栗子
setTimeout(function() {
console.log('setTimeout')
})
new Promise(function(resolve) {
console.log('promise')
resolve()
}).then(function() {
console.log('then')
})
console.log('console')
复制代码
- 这段代码会作为宏任务,进入主线程
- 先遇到setTimeout,将其放入Event Table,完成后会将其回调函数注册并放入到宏任务Event Queue
- 接下来遇到了Promise,new Promise立即执行,然后遇到console.log('promise'),立即执行。then函数放入微任务Event Queue。
- 遇到console.log('console'),立即执行
- 整体代码作为第一个宏任务执行结束,接着查看是否有微任务?我们发现了then在微任务Event Queue里面,执行
- 微任务全部执行完毕,第一轮事件循环结束,开始第二轮循环。从宏任务Event Queue发现有setTimeout对应的回调函数(多个宏任务的时候取队头),立即执行
- 检查无可执行任务,结束