一、简介

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'

四、参考文献

https://github.com/febobo/web...

03-05 14:08