返回

用通俗易懂的语言理解 JavaScript 中的事件循环

前端

事件循环概述

JavaScript 是一种单线程语言,这意味着它一次只能执行一个任务。然而,它却可以同时处理多个任务,这要归功于事件循环。事件循环是一个不断运行的进程,它不断地检查是否有新的事件需要处理,如果有,它就会将这些事件放入队列中,并等待主线程空闲时再执行它们。

堆与栈

在理解事件循环之前,我们首先需要了解堆和栈的概念。堆和栈是计算机内存中的两个不同区域,它们分别用于存储不同的数据类型。

堆用于存储对象,而栈用于存储函数调用信息,例如局部变量和参数。当一个对象被创建时,它就会被分配到堆中,而当一个函数被调用时,它的调用信息就会被压入栈中。当函数调用结束后,它的调用信息就会从栈中弹出。

事件队列

事件队列是一个 FIFO(先进先出)队列,它存储着即将被执行的事件。当一个事件被触发时,它就会被添加到事件队列中。事件循环会不断地检查事件队列,如果有事件等待执行,它就会将该事件从队列中取出并交由主线程执行。

消息队列

消息队列是一个 FIFO 队列,它存储着即将被执行的宏任务。宏任务是指那些需要较长时间才能执行的任务,例如网络请求和 setTimeout 函数。当一个宏任务被触发时,它就会被添加到消息队列中。事件循环会不断地检查消息队列,如果有宏任务等待执行,它就会将该宏任务从队列中取出并交由主线程执行。

回调函数

回调函数是指当某个事件发生时被调用的函数。回调函数通常被用作事件处理程序,当事件发生时,就会触发回调函数的执行。例如,当一个按钮被点击时,就会触发一个点击事件,而这个事件的处理程序就是一个回调函数。

宏任务和微任务

宏任务和微任务都是需要被执行的任务,但它们之间存在着一些关键的区别。宏任务是指那些需要较长时间才能执行的任务,例如网络请求和 setTimeout 函数。而微任务是指那些不需要花费太多时间就能执行的任务,例如 Promise.then() 函数和 MutationObserver 函数。

宏任务和微任务都会被添加到消息队列中,但微任务的优先级更高。这意味着,当事件循环检查消息队列时,它会先执行所有的微任务,然后再执行宏任务。

理解事件循环

现在,我们已经了解了堆、栈、事件队列、消息队列、回调函数、宏任务和微任务的概念,我们可以开始理解事件循环的工作原理了。

事件循环是一个不断运行的进程,它不断地检查是否有新的事件需要处理,如果有,它就会将这些事件放入队列中,并等待主线程空闲时再执行它们。

当主线程空闲时,它会首先检查事件队列,如果有事件等待执行,它就会将该事件从队列中取出并执行它。当事件执行完成后,主线程就会继续执行下一个事件。

如果在执行事件的过程中,触发了新的事件,这些新的事件就会被添加到事件队列中,等待主线程空闲时执行。

当主线程执行完所有事件后,它就会检查消息队列,如果有宏任务等待执行,它就会将该宏任务从队列中取出并执行它。当宏任务执行完成后,主线程就会继续执行下一个宏任务。

如果在执行宏任务的过程中,触发了新的宏任务,这些新的宏任务就会被添加到消息队列中,等待主线程空闲时执行。

事件循环不断地重复这个过程,直到所有的事件和宏任务都执行完毕。

总结

JavaScript 的事件循环是一个复杂的概念,但它是理解 JavaScript 异步编程的基础。通过本文的介绍,您应该对事件循环有了初步的认识。如果您想了解更多关于事件循环的内容,可以参考本文提供的参考资料。