前言:JavaScript是一种单线程的,为了利用好资源和任务分配,JS将任务分为同步任务和异步任务,而事件循环就是规定了执行任务的顺序。

Event Loop

事件循环机制就是按照以下的顺序模式来循环执行JS中的任务事件

顺序如下:

  1. 同步先执行
  2. 异步(微任务 > dom渲染 > 宏任务,然后又继续去找有没微任务)

微任务

  • promise.then(new promise构造函数是同步)、async/await。
  • process.nextTick
  • Promise.catch
  • resove/reject
  • MutationObserver

注意

Promise是异步的,是指他的then()和catch()方法,Promise本身还是同步的,所以遇到Promise还是先执行的Promise同步代码。(同步优先)

宏任务

  • setTimeout
  • setInterval
  • setImmediate
  • ajax请求
  • dom事件
  • script块

总结

  1. 先执行同步代码,
  2. 遇到异步宏任务则将异步宏任务放入宏任务队列中,
  3. 遇到异步微任务则将异步微任务放入微任务队列中,
  4. 当所有同步代码执行完毕后,再将异步微任务从【队列】中调入【主线程】执行,
  5. 微任务执行完毕后再将异步宏任务从【队列】中调入【主线程】执行,
    一直循环直至所有任务执行完毕。

同步(Promise)>异步(微任务(process.nextTick ,Promises.then,Promise.catch ,resove/reject,MutationObserver) > 宏任务(setTimeout,setInterval,setImmediate))

注意 new Promise() 是同步方法,resolve才是异步方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 1.程序开头,主线程,先执行,输出'script start'
console.log('script start')

// 3.执行函数
async function async1() {
// 3.1 执行await函数async2,也就是promise.then()
// 3.4 执行完第一个await函数之后,微任务队列中还有一个任务,暂时跳出async函数
// 3.5 输出'async1 end',保留async1函数的上下文,然后跳出async1函数
await async2()
// 5.拿回执行权,输出
console.log('async1 end')
}

async function async2() {
// 3.2 开始执行任务,输出 'async2 end'
console.log('async2 end')

// 3.3 promise.resolve().then(),先加入微任务队列中,等待下一轮事件循环执行
return Promise.resolve().then(()=>{
console.log('async2 end1')
})
}

// 2.开始调用async函数
async1()

// 所有微任务执行完,开始执行宏任务
setTimeout(function() {
console.log('setTimeout')
}, 0)

// 4 new Promise是同步的,先执行同步
new Promise(resolve => {
// 4.1 输出 'promise',执行完这轮同步操作,继续往下看,还有没有同步的
console.log('Promise')
resolve()
}) // 4.1 出现promise.then(),加入微任务队列,那么队列中就有两个微任务,按照先进先出的原则来执行,这一轮循环会先执行3.3 中的微任务,但是还是先执行同步任务先
.then(function() {
console.log('promise1')
}) // 4.4 执行完上一个then()之后又产生一个新的微任务,继续输出,当前微任务队列执行完毕。执行权回到async1
.then(function() {
console.log('promise2')
})

// 4.3 有同步任务,先执行同步任务,输出 'script end' ,再执行异步
console.log('script end')