一、简介
javaScript是单线程非阻塞的脚本语言。
单线程
单线程是指在执行JavaScript代码的时候,主线程按照顺序执行。
非阻塞
非阻塞是指执行异步任务的时候,主线程会挂起任务,等待异步任务返回结果后按顺序执行
事件循环
同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
异步任务:异步执行的任务,异步任务又分为微任务(micorotask)和宏任务(macrotack)
同步任务进入主线程,即主执行栈,异步任务进入任务队列,主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。上述过程的不断重复就是事件循环
二、宏任务与微任务
异步任务又分为微任务(micorotask)和宏任务(macrotack)
微任务
在当前宏任务执行结束之后立即执行的任务,执行完微任务在执行下一个宏任务。
常见的微任务:Promise.then
Object.observe
MutationObserver
process.nextTick(ndoe环境)
宏任务
每次执行栈执行的任务就是宏任务(包括从任务队列中获取下一个宏任务队列去执行)。
常见的宏任务:script代码
setTimeout
setInterval
I/O
UI交互事件
postMessage
三、代码分析
实例代码
console.log('script start')
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
return 'async then'
}
async function async2() {
console.log('async2 end')
}
async1()
setTimeout(function() {
console.log('setTimeout')
}, 0)
async1().then(function (message) { console.log(message) })
new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})
console.log('script end')
流程步骤
1、遇到console直接打印 'script start'
2、执行async1,打印console('async1 start'
),遇到await阻塞async2后面的流程。
3、执行async2,打印 'async2 end'
,async2后面的代码留着后面执行(任务队列:async1)
4、遇到定时器,属于新的宏任务,留着后面执行(任务队列:async1、定时器)
5、执行async1,打印出'async1 start'
、 'async2 end'
,
6、.then 属于微任务,放入微任务队列,后面再执行(任务队列:async1、then、定时器)
7、遇到 new Promise,这个是直接执行的,打印 'Promise'
8、.then 属于微任务,放入微任务队列,后面再执行(任务队列:async1、then(async1)、then(promise)、定时器)
9、遇到console直接打印 'script end'
执行任务队列宏任务(async1、then、then、定时器)
1、执行async2后面的代码,打印 'async1 end'
2、执行async1 async2后面的代码,打印 'async1 end'
,then放入微任务队列
3、执行promise 第一个then事件,打印 'promise1'
第二个then放入微任务队列
4、执行微任务队列async1 then事件,打印 'async then'
5、执行promise第二个then事件,打印 'promise2'
6、最后执行定时器 'setTimeout'
输出结果:
'script start'
'async1 start'
'async2 end'
'async1 start'
'async2 end'
'Promise'
'script end'
'async1 end'
'async1 end'
'promise1'
'async then'
'promise2'
'setTimeout'