Javascript 是一种单线程语言,这意味着它一次只能执行一个任务。但是,它仍然设法同时执行多项任务。它通过使用一些复杂的数据结构给人一种多线程的错觉。为实现这一点,Javascript 引擎有一个称为事件循环的重要组件。我们将了解什么是事件循环以及它如何在不阻塞主线程的情况下处理异步任务。
什么是事件循环?
事件循环是 Javascript 中的一种机制,可以执行非阻塞异步操作。它允许 Javascript 在不阻塞主线程的情况下处理诸如从服务器获取数据、发出 HTTP 请求和处理用户事件等任务。
根据MDN Doc,它是一个运行时模型,它执行代码,收集和处理事件,并执行排队的子任务。了解事件循环的工作原理对于编写高效和高性能的代码至关重要。
为了更好地理解事件循环,让我们列出用于执行异步代码的组件 -
- 调用堆栈:JavaScript 使用调用堆栈来跟踪当前正在执行的函数(执行上下文)。当一个函数被调用时,它被添加到堆栈中,当它返回时,它被从堆栈中删除。
- Web API:Web API 由浏览器或 JavaScript 运行时环境提供,并提供 DOM 操作、计时器(setTimeout、setInterval)、XMLHttpRequest 等功能。这些 API 异步处理耗时的任务。JavaScript 与 Web API 交互,例如 DOM API、XMLHttpRequest 或 setTimeout,它们提供 JavaScript 引擎之外的功能。
- 任务队列:任务队列(也称为回调队列)保存准备好由事件循环处理的任务。当相关的异步操作完成时,这些任务就会入队。异步操作,例如计时器、用户事件和网络请求,由 Web API 处理。一旦这些操作完成,它们就会被放入任务队列中。
- 事件循环:事件循环不断检查两件事:调用堆栈和任务队列。如果 Call Stack 为空,则从 Task Queue 中取出第一个任务,并将其推送到 Call Stack 中执行。
通过代码示例了解事件循环
代码语言:javascript复制console.log('Start');
setTimeout(function() {
console.log('Inside setTimeout');
}, 0);
console.log('End');
在这里,我们有 3 个控制台日志语句。但是其中一个控制台日志是在setTimeout
Web API 中定义的。此 Web API 会将计时器设置为设置为setTimeout
(此处我们已给出0ms
)的值,一旦时间完成,setTimeout
Web API 会将回调发送到任务队列。现在,它会一直留在那里直到邮件线程被释放,也就是说,执行堆栈变空。
这里需要注意的一点是,即使时间设置成0ms
中的setTimeout
,也会在最后执行。这是因为它setTimeout
是一个带有定时器的异步任务,必须进入队列,然后等待主线程空闲。这个定时器可以是0 ms
或10000 ms
无论如何,它仍然会被注册到任务队列中。下面的可视化图表清楚地解释了这一点——
动图
现在更清楚了,对吧?所以,这就是异步任务的工作方式。请注意,附加到 setTimeout 的时间是最小的,即代码至少不会在设置的时间内运行。但是只有在主线程释放后才会执行。假设您有一个 1000 毫秒的 setTimeout,但由于执行了一些复杂的操作,主线程花费了 2000 毫秒。在这种情况下,注册的 setTimeout 只能在 2000 毫秒后执行,而不是在 1000 毫秒后立即执行!
让我们深入研究下一个例子。我们将使用 XMLHttpRequest
代码语言:javascript复制console.log('Start');
var request = new XMLHttpRequest();
request.open('GET', 'https://api.example.com/data', true);
request.onreadystatechange = function() {
if (request.readyState === 4 && request.status === 200) {
console.log('Data received:', request.responseText);
}
};
request.send();
console.log('End');
我希望从前面的例子中你已经解码了上面的代码是如何工作的。是的,Http 异步请求将由 Web API 处理XMLHttpRequest
。它将被处理并发送到任务队列中。Event Loop会一直等到主线程空闲,然后将任务Dqueue到Task Queue里面,放到Execution Stack中,由主线程执行。通过这个解释,我们可以说输出将是 -
Start
End
Data received: xyz // xyz is any response that API has sent
结论
了解 JavaScript 事件循环对于编写高效且响应迅速的 JavaScript 代码至关重要。通过掌握其内部工作原理以及调用堆栈、Web API、任务队列和事件循环等组件的作用,您可以自信地处理异步任务并构建高性能的 Web 应用程序。有了这些知识,您就可以很好地处理复杂的场景并充分利用 JavaScript 的异步特性。