进程&线程
- 浏览器是多线程的,JS是单线程的(浏览器只分配一个线程来执行JS)
- 进程大线程小:一个进程中包含多个线程,例如在浏览器中打开一个HTML页面就占用了一个进程,加载页面的时候,浏览器分配一个线程去计算DOM树,分配其它的线程去加载对应的资源文件...再分配一个线程去自上而下执行JS
同步&异步
- 同步:在一个线程上(主栈/主任务队列)同一个时间只能做一件事情,当前事情完成才能进行下一个事情(先把一个任务进栈执行,执行完成,在把下一个任务进栈,上一个任务出栈...)
同步任务主要包含:console.log、
async
函数中await()
方法之前的代码、Promise中的代码
- 异步:在主栈中执行一个任务,但是发现这个任务是一个异步的操作,我们会把它移除主栈,放到等待任务队列中,此时浏览器会分配其它线程监听异步任务是否到达指定的执行时间,如果执行完成,监听者会把到达时间的异步任务重新放到主栈中执行...
宏任务(Tasks/Macrotasks)&微任务(Microtasks)
- 宏任务:可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)
浏览器为了能够使得JS内部task与DOM任务能够有序的执行,会在一个task执行结束后,在下一个(macro)task 执行开始前,对页面进行重新渲染,流程如下:
task主要包含:script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)
- 微任务: 可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前task任务后,下一个task之前,在渲染之前。
所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染。也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask都执行完毕(在渲染前)
microtask主要包含:Promise.then、MutaionObserver、process.nextTick(Node.js 环境)、
async
函数中await()
方法之后的代码、Promise中的then和catch
小结:
- 宏任务按顺序执行,浏览器会在当前任务与下一个任务之间渲染
- 微任务也按顺序执行,执行情况有两种:
1 只要当前没有可执行的js在栈中,它就会在每个callback回调之后执行
2 在每一个任务执行完成,从栈中移除后
运行机制
在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下
- 执行一个宏任务(栈中没有就从事件队列中获取)
- 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
- 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
- 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
- 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
流程图如下:
第一题
var p = new Promise((resolve, reject) => {
console.log('promise 构造函数')
setTimeout(() => {
console.log('promise 构造函数里的定时任务')
resolve('promise 构造函数里的定时任务的resovle结果')
}, 2000)
})
p.then((res) => {
console.log('promise then 方法')
console.log('promise then 方法的结果: ', res)
})
setTimeout(() => {
console.log('定时任务')
}, 0)
结果:
promise 构造函数
定时任务
promise 构造函数里的定时任务
promise then 方法
promise then 方法的结果: promise 构造函数里的定时任务的resovle结果
第二题
var p = new Promise((resolve, reject) => {
console.log('promise 构造函数')
setTimeout(() => {
console.log('promise 构造函数里的定时任务')
resolve('promise 构造函数里的定时任务的resovle结果')
}, 1)
})
p.then((res) => {
console.log('promise then 方法')
console.log('promise then 方法的结果: ', res)
})
setTimeout(() => {
console.log('定时任务')
}, 0)
结果:
promise 构造函数
promise 构造函数里的定时任务
promise then 方法
promise then 方法的结果: promise 构造函数里的定时任务的resovle结果
定时任务
第三题
async function a1 () {
console.log('a1 start')
await a2()
console.log('a1 end')
}
async function a2 () {
console.log('a2')
}
console.log('script start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
Promise.resolve().then(() => {
console.log('promise1')
})
a1()
let promise2 = new Promise((resolve) => {
resolve('promise2.then')
console.log('promise2')
})
promise2.then((res) => {
console.log(res)
Promise.resolve().then(() => {
console.log('promise3')
})
})
console.log('script end')
结果:
script start
a1 start
a2
promise2
script end
promise1
a1 end
promise2.then
promise3
setTimeout
参考博文:
英文文章:https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
简书文章:https://www.jianshu.com/p/9644f13568c0