深入理解 JavaScript 进程、线程与事件循环
进程 & 线程
在计算机科学中,进程是指一个程序在运行过程中分配和管理资源的基本单位。而线程则是进程中更小的单位,负责执行一段指令。简单来说,进程是程序的一次执行,而线程是在进程中执行的一个单独的路径。
- 进程:指的是 CPU 运行指令和保存上下文所需的时间。
- 线程:是进程中更小的单位,指的是一段指令执行所需要的时间。
浏览器新开一个 Tab 页(进程)
当你在浏览器中新开一个 Tab 页时,涉及到多个线程协同工作,以保证流畅的用户体验。
- 渲染线程:负责将 HTML、CSS 和 JavaScript 转换为可视化页面,同时处理用户交互。
- HTTP 请求线程:用于处理发送和接收 HTTP 请求的线程。
- JavaScript 引擎线程:执行 JavaScript 代码的线程,处理与 JavaScript 相关的任务。
线程之间可以协同工作,但渲染线程和 JavaScript 引擎线程是互斥的,防止发生数据竞争和不一致的情况。
JavaScript 是单线程的
JavaScript 是一种单线程的语言,意味着它一次只能执行一个任务。这带来了一些优势:
- 节约内存:不需要为多线程分配额外的内存,减少了内存占用。
- 无锁概念:由于是单线程,不存在多线程间的数据竞争,也就没有锁的概念,减少了上下文切换的时间。
异步
在 JavaScript 中,异步是一种非阻塞的执行方式,通过回调函数、Promise、Async/Await 等机制实现。
- 宏任务(macrotask) :包括 script 代码、setTimeout、setInterval、setImmediate、I/O 操作和 UI 渲染等。
- 微任务(microtask) :包括 Promise 的 then 方法、MutationObserver 和 process.nextTick()。
Event-Loop(浏览器角度)
浏览器的 Event-Loop 是一个重要的概念,描述了 JavaScript 的执行顺序。 它会按以下步骤执行
- 执行同步代码:同步代码属于宏任务,会按照顺序执行。
- 执行栈为空时:当执行栈为空时,检查是否有需要执行的异步任务。
- 执行微任务:先执行微任务队列中的任务,确保微任务在下一轮 Event-Loop 之前执行。
- 渲染页面:如果需要,会渲染页面以保证用户界面更新。
- 执行宏任务:执行宏任务队列中的任务,开始下一轮 Event-Loop。
接下来我们通过一些代码来理解事件循环(以下代码没有渲染这一步)
tips: 当v8 在碰到微任务和宏任务时,会创建一个微任务队列和一个宏任务队列,执行完同步代码后,便会按以上步骤继续执行。
console.log('start');
async function async1() {
await async2()
//await 一脚把后续代码踹到微任务队列里,它自己也是微任务。但是浏览器给await开小灶提速,它会直接执行
console.log('saync1 end');
}
async function async2() {
console.log('saync2 end');
}
async1() //调用函数 (步骤1)
setTimeout(function () {
console.log('setTimeout');
}, 0)
new Promise((resolve) => {
console.log('promise');
resolve()
})
.then(() => {
console.log('then1');
})
.then(() => {
console.log('then2');
})
console.log('end');
// 如此梳理:
// 代码行1:打印顺序 1 (步骤1)
// 2:入微任务队列
// 7:入微任务队列
// 10:调用函数(步骤1)
// 3:立即执行
// 8:打印顺序 2 (步骤1)
// 4:因为await的原因不再是同步代码,入微任务队列
// 11:入宏任务队列
// 14:(new一个对象是同步代码)
// 15:打印顺序 3 (步骤1)
// 16:调用函数。(then 可以执行,但它是微任务)
// 24:打印顺序 4 (步骤1)
// 步骤1就执行完了,接着步骤2,检查是否有异步任务,先把微任务队列里执行完,按照先入先出的原则,
// 接着再执行宏任务队列里的异步代码。当执行到第11行代码时,此时正在执行步骤5,
// **既是本次事件的结束,也是下次事件的开始**。这便是事件循环
// 所以打印顺序为:'start' 'saync2 end' 'promise' 'end' 'saync1 end' 'then1' 'then2' 'setTimeout'
建议搭配前一篇文章观看,效果更佳哦 juejin.cn/post/730823…
深入理解 JavaScript 的进程、线程以及事件循环机制有助于更好地编写高效、非阻塞的代码,提升 Web 应用的性能和用户体验