【javaScript】事件循环机制

事件循环机制

相关文章

事件循环机制


一、JavaScript 的事件循环是什么

JavaScript 的事件循环(Event Loop)机制是一种用于管理异步任务执行顺序的机制。它保证了 JavaScript 单线程环境下的代码执行顺序,并且能够处理异步任务的执行。

事件循环机制由以下几个重要的组成部分构成:

  • 调用栈(Call Stack):

    • JavaScript 引擎使用调用栈来追踪代码的执行过程。每当调用一个函数时,该函数会被压入调用栈中,当函数执行完成后,会从调用栈中弹出。这种先进后出(LIFO)的方式确保了代码的顺序执行。
  • 任务队列(Task Queue):

    • 任务队列用于存放异步任务的回调函数。包括宏任务队列(macrotask queue)和微任务队列(microtask queue)两种。异步任务的回调函数被添加到任务队列中,等待事件循环将它们取出并执行。
  • 事件循环(Event Loop):

    • 事件循环负责从任务队列中取出任务,并将其放入调用栈中执行。事件循环的工作原理是不断地从任务队列中取出任务,如果调用栈为空,则将任务放入调用栈中执行。循环过程会不断重复,直到所有任务都被执行完毕。

二、事件循环的执行过程:

  • 执行同步代码:

    • 首先,JavaScript 引擎会执行主线程中的同步代码,将函数调用和执行过程压入调用栈中。
  • 处理微任务:

    • 当主线程中的同步代码执行完成后,JavaScript 引擎会检查微任务队列(Microtask Queue)。如果微任务队列不为空,会依次取出微任务并将其放入调用栈中执行,直到微任务队列为空为止。
  • 处理宏任务:

    • 接着,JavaScript 引擎会检查宏任务队列(Macrotask Queue)。如果宏任务队列中有任务,则取出一个任务并将其放入调用栈中执行。执行完一个宏任务后,回到第二步继续处理微任务。
  • 重复执行:

    • 微任务执行完毕后,JavaScript 引擎会再次检查微任务队列,如果有新的微任务,则继续执行微任务。随后继续检查宏任务队列,执行宏任务。这个过程会持续循环执行,直到任务队列中没有任务为止,事件循环结束。

三、宏任务有哪些

  • setTimeout 和 setInterval:

    • 使用 setTimeout 和 setInterval 函数可以创建宏任务,它们可以在指定的时间间隔后执行回调函数。
  • DOM 事件:

    • 用户交互事件(如点击、鼠标移动、键盘输入等)和其他 DOM 事件(如加载、改变大小、滚动等)都会触发宏任务。
  • Ajax 请求:

    • 发送 Ajax 请求时,回调函数会在请求完成后被添加到宏任务队列中,等待执行。
  • 页面加载:

    • 页面加载、资源加载(如图片、样式表、脚本等)也会触发宏任务。
  • I/O 操作:

    • 读写文件、网络请求等 I/O 操作都是宏任务,它们会在操作完成后将回调函数添加到宏任务队列中。
  • requestAnimationFrame:

    • 使用 requestAnimationFrame 可以创建一个在下次浏览器重绘之前执行的宏任务,通常用于实现动画效果。

四、微任务有哪些

  • Promise 的回调函数:

    • 当 Promise 对象状态变为 resolved 或 rejected 时,会执行与之关联的回调函数,这些回调函数会作为微任务被添加到微任务队列中。
  • MutationObserver:

    • 使用 MutationObserver 监听 DOM 的变化,当被监视的 DOM 被修改时,会触发回调函数,该回调函数会作为微任务被添加到微任务队列中。
  • process.nextTick(仅在 Node.js 中):

    • 在 Node.js 环境中,使用 process.nextTick 可以创建一个在当前操作结束后立即执行的微任务。
  • queueMicrotask API:

    • 在浏览器中,可以使用 queueMicrotask API 来添加一个微任务到微任务队列中,以在下一个事件循环迭代时执行。

五、示例

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
console.log(1);

setTimeout(function() {
console.log(2);

Promise.resolve().then(function() {
console.log(3);
});

console.log(4);
}, 0);

Promise.resolve().then(function() {
console.log(5);

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

Promise.resolve().then(function() {
console.log(7);
});

console.log(8);
});

console.log(9);
  • 第一轮事件循环

    • 执行同步任务
    1
    console.log(1);
    • 注册宏任务
    1
    2
    3
    4
    5
    6
    7
    8
    9
    setTimeout(function() {
    console.log(2);

    Promise.resolve().then(function() {
    console.log(3);
    });

    console.log(4);
    }, 0);
    • 注册微任务
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Promise.resolve().then(function() {
    console.log(5);

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

    Promise.resolve().then(function() {
    console.log(7);
    });

    console.log(8);
    });
    • 执行同步任务
    1
    console.log(9);

    第一轮事件循环结束,输出:1、9。

    第一轮事件循环执行很清晰,主要是执行所有同步任务,注册微任务。

  • 第二轮事件循环

    • 执行新产生的所有微任务
    1
    2
    3
    4
    5
    6
    7
    Promise.resolve().then(function() {

    console.log(5);

    console.log(8);

    });
    • 注册宏任务
    1
    2
    3
    setTimeout(function() {
    console.log(6);
    }, 0);
    • 注册微任务
    1
    2
    3
    Promise.resolve().then(function() {
    console.log(7);
    });

    第二轮事件循环结束,输出:5、8

  • 第三轮事件循环

    • 执行新产生的所有微任务
    1
    2
    3
    Promise.resolve().then(function() {
    console.log(7);
    });
    • 执行队列中的第一个宏任务
    1
    2
    3
    4
    5
    setTimeout(function() {
    console.log(2);

    console.log(4);
    }, 0);

    第三轮事件循环结束,输出:7、2、4

  • 第四轮事件循环

    • 执行新产生的所有微任务
    1
    2
    3
    4
    Promise.resolve().then(function() {
    console.log(3);
    });

    • 执行队列中的第一个宏任务
    1
    2
    3
    setTimeout(function() {
    console.log(6);
    }, 0);

    第三轮事件循环结束,输出:3、6

事件循环结束:完整输出:1、9、5、8、7、2、4、3、6


喜欢这篇文章?打赏一下支持一下作者吧!
【javaScript】事件循环机制
https://www.cccccl.com/20210502/javascript/事件循环机制/
作者
Jeffrey
发布于
2021年5月2日
许可协议