2025-02-26
生命不是安排,而是追求。——弗吉尼亚·伍尔芙
从线程和浏览器底层执行的角度来看,JavaScript 的延迟/休眠功能是如何实现的呢?
线程模型和事件循环
JavaScript 的执行环境(如浏览器或 Node.js)是单线程的,这意味着在任何给定的时间点,只有一个线程在执行 JavaScript 代码。为了管理并发操作,JavaScript 依赖于事件循环(Event Loop)。
事件循环
事件循环是一个不断检查和处理消息队列的机制。它会执行以下步骤:
- 检查调用栈(Call Stack),如果调用栈为空,则继续。
- 检查消息队列(Message Queue),如果消息队列中有待处理的任务,则取出队首的任务并执行。
- 重复以上步骤。
宏任务和微任务
在事件循环中,有两种类型的任务:宏任务(Macro Task)和微任务(Micro Task)。
- 宏任务:包括整体脚本执行、
setTimeout
、setInterval
、setImmediate
(Node.js)等。 - 微任务:包括
Promise
的回调、process.nextTick
(Node.js)、MutationObserver
等。
事件循环会优先处理微任务队列中的任务,然后再处理宏任务队列中的任务。
延迟/休眠的实现
JavaScript 没有直接的 sleep 函数,但可以通过 setTimeout
和 Promise
来实现延迟/休眠功能。
setTimeout
setTimeout
是一种宏任务,会在指定的时间后将回调函数添加到事件队列中。
1 |
|
上面的 sleep
函数返回一个 Promise
,该 Promise
在指定的毫秒数之后解决(resolve)。
async/await
结合 async/await
,可以在异步函数中实现类似同步代码的延迟效果:
1 |
|
在这个例子中,demo
函数会先打印 “Start”,然后等待 2 秒,最后打印 “End”。
浏览器底层执行
在浏览器中,JavaScript 引擎(如 V8 引擎)和浏览器内核(如 Chromium 内核)协同工作来处理异步操作。
任务调度
当调用 setTimeout
时,浏览器内核会将此任务调度到定时器队列中。当定时器到期时,回调函数会被添加到事件队列中等待执行。
事件循环与渲染
浏览器的事件循环还包括了渲染步骤。在每个事件循环迭代中,浏览器会在处理任务之前进行渲染更新。这意味着即使在延迟期间,浏览器也会继续绘制和更新用户界面。
结论
通过了解 JavaScript 的线程模型和事件循环机制,我们可以更好地理解如何实现延迟/休眠功能。利用 setTimeout
和 Promise
,可以在不阻塞主线程的情况下实现延迟执行,从而保证用户界面依然响应迅速。