# Event Loop (浏览器端)

# js 引擎是单线程,也就是说每次只能执行一项任务,其他任务都得按照顺序排队等待被执行,只有当前的任务执行完成之后才会往下执行下一个任务。

# 1、es6之前的event loop(同步任务、异步任务)

# 同步任务(同步代码)、异步任务(DOM操作相关、定时器相关回调、ajax回调):

  • 同步任务会在执行栈(先进后出)中按照顺序排队等待主线程的执行,当碰到异步任务时会挂起,在异步有了结果之后将回调加入消息队列(先进先出)中等待主线程空闲时,被读取到执行栈中等待主线程执行

  • 示例代码

console.log(1); // 同步任务

setTimeout(() => { // 异步任务,先挂起
  console.log(2);
});

console.log(3); // 同步任务

// 输出顺序: 1、3、2
1
2
3
4
5
6
7
8
9

# 2、es6之后的event loop(宏任务、微任务)

# es6之后将同步任务、定时器回调相关、ajax回调、IO、UI Render都归属于宏任务(tasks),同时浏览器端Promise回调、Object.observe、MutationObserver归属于微任务(jobs)

  • 宏任务主代码块(script)按照顺序执行,当碰到宏任务中的异步任务时也是会被挂起,在异步有了结果之后将回调加入宏任务队列,主代码块查询是否有需要执行的微任务,有的话执行所有微任务,当主线程空闲时,异步任务被读取到执行栈中执行,然后查询是否有需要执行的微任务,有的话执行所有微任务
console.log(1); // 主代码块宏任务

setTimeout(() => { // setTimeout宏任务,先挂起
  new Promise((resolve, reject) => {
    console.log(6); // setTimeout宏任务
    resolve();
  }).then(() => {
    console.log(7) // setTimeout微任务
  })
  console.log(2); // setTimeout宏任务
});

new Promise((resolve, reject) => {
  console.log(3); // 主代码块宏任务
  resolve();
}).then(() => {
  console.log(4) // 主代码块微任务
})
console.log(5); // 主代码块宏任务

// 输出顺序: 1、3、5、4、6、2、7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 3.扩展题

console.log(1); // 主代码块宏任务

const fn = async()=>{
  await (()=>{});
  await (console.log(2))
}
fn();
console.log(3);

// 输出顺序: 1、3、2
1
2
3
4
5
6
7
8
9
10
console.log(1); // 主代码块宏任务

const fn = async()=>{
  // await (()=>{});
  await (console.log(2))
}
fn();
console.log(3);

// 输出顺序: 1、2、3
1
2
3
4
5
6
7
8
9
10

为什么两次的输出结果不一样,原因就是async await,首次await之后才开始为微任务,可以把代码转化成:

console.log(1); // 主代码块宏任务

const fn = Promise.resolve(console.log(2)).then(() =>{
  
})
fn();
console.log(3);

// 输出顺序: 1、2、3
1
2
3
4
5
6
7
8
9