Skip to content
导航栏

事件循环

0. 进程和线程

  • 进程是CPU资源分配的最小单位,线程是CPU调度的最小单位。
  • 一个进程可以包含多个线程,但至少有一个线程。
  • JavaScript是单线程语言,它只有一个主线程,所有任务都在这个主线程上执行,其他线程都在后台配合。

1. 什么是JavaScript事件循环

  • JavaScript是一门单线程语言,它的所有任务都需要在一个线程上完成,所以它的运行机制称为事件循环(Event Loop)。
  • JavaScript的运行机制是,当主线程运行时,其他任务都会在后台运行,等主线程运行完毕,再执行后台任务。
  • JavaScript的事件循环机制就是,主线程从“任务队列”中读取事件,这个过程是循环不断的,所以整个的运行机制称为“Event Loop”(事件循环)。
  • 事件循环的每一轮称为一个tick,每一轮的任务处理模型是一样的,只是处理的事件队列不一样。
  • 事件循环的运行机制如下:
    • 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
    • 主线程之外,还存在一个“任务队列”(task queue)。只要异步任务有了运行结果,就在“任务队列”之中放置一个事件。
    • 一旦“执行栈”中的所有同步任务执行完毕,系统就会读取“任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
    • 主线程不断重复上面的第三步。
    • 主线程和任务队列是绝对的异步,不会互相干扰。

2. 宏任务和微任务

  • 宏任务(macro-task):包括整体代码script,setTimeout,setInterval,setImmediate,I/O,UI交互事件,postMessage,MessageChannel,setImmediate(Node.js 环境)
  • 微任务(micro-task):Promise,process.nextTick(Node.js 环境)
  • 宏任务和微任务的区别在于,宏任务是在当前执行栈执行的任务,微任务是在微任务队列中的任务,当前执行栈执行完毕后立即执行微任务队列中的任务。

3. 事件循环的执行顺序(题目)

3.1 题目一

js
setTimeout(() => {
  console.log("setTimeout")
}, 1000)

queueMicrotask(() => {
  console.log("queueMicrotask")
})

Promise.resolve().then(() => {
  console.log("Promise then")
})

function foo() {
  console.log("foo")
}

function bar() {          
  console.log("bar")
  foo()
}

bar()

console.log("其他代码")

// bar
// foo
// 其他代码
// queueMicrotask
// promise then
// setTimeout

3.2 题目二

js
setTimeout(function () {
  console.log("setTimeout1");
  new Promise(function (resolve) {
    resolve();
  }).then(function () {
    new Promise(function (resolve) {
      resolve();
    }).then(function () {
      console.log("then4");
    });
    console.log("then2");
  });
});

new Promise(function (resolve) {
  console.log("promise1");
  resolve();
}).then(function () {
  console.log("then1");
});

setTimeout(function () {
  console.log("setTimeout2");
});

console.log(2);

queueMicrotask(() => {
  console.log("queueMicrotask1")
});

new Promise(function (resolve) {
  resolve();
}).then(function () {
  console.log("then3");
});

// promise1
// 2
// then1
// queueMicrotask1
// then3
// setTimeout1
// then2
// then4
// setTimeout2

3.3 题目三

js
async function bar() {
  console.log("22222")
  return new Promise((resolve) => {
    resolve()
  })
}

async function foo() {
  console.log("111111")

  await bar()

  console.log("33333")
}

foo()
console.log("444444")

// 1111
// 2222
// 4444
// 3333

3.4 题目四

js
async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}

async function async2() {
  console.log("async2");
}

console.log("script start");

setTimeout(function () {
  console.log("setTimeout");
}, 0);

async1();

new Promise(function (resolve) {
  console.log("promise1");
  resolve();
}).then(function () {
  console.log("promise2");
});

console.log("script end");

// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout

3.5 题目五

js
async function async1() {
  console.log('async1 start')
  await async2()
  console.log('async1 end')
}

async function async2() {
  console.log('async2')
}

console.log('script start')

setTimeout(function () {
  console.log('setTimeout0')
}, 0)

setTimeout(function () {
  console.log('setTimeout2')
}, 300)

setImmediate(() => console.log('setImmediate'));

process.nextTick(() => console.log('nextTick1'));

async1();

process.nextTick(() => console.log('nextTick2'));

new Promise(function (resolve) {
  console.log('promise1')
  resolve();
  console.log('promise2')
}).then(function () {
  console.log('promise3')
})

console.log('script end')

// script start
// async1 start 
// async2
// promise1
// promise2
// script end
// nextTick1
// nextTick2
// async1 end
// promise3
// setTimeout0
// setImmediate
// setTimeout2