JavaScript 单线程原理与异步编程机制

笔记哥 / 04-16 / 38点赞 / 0评论 / 324阅读
## JavaScript 单线程原理与异步编程机制 ### 为什么 JavaScript 是单线程? JavaScript 被设计成单线程,简单来说就是 —— **浏览器里干活儿只能一个接一个排着队来,没法同时多开窗口摸鱼。** #### 举个栗子: 你点按钮 → 网页要弹个提示 → 这时候如果网页还在加载数据 → 弹提示就得等加载完 → **单线程 = 一次只能干一件事**。 #### 为啥这么设计? - 最初网页交互简单(填表单、点按钮),单线程够用。 - 避免多线程打架(比如两个线程同时改同一个按钮的状态)。 #### 单线程的优点: - **开发简单**:避免了多线程中的数据竞争、死锁等复杂问题。 - **调试方便**:执行顺序清晰明确,便于排查问题。 - **适合 I/O 密集型任务**:大多数 JS 任务(如事件处理、请求响应)并不需要多核计算资源。 #### 单线程的缺点: - **阻塞风险高**:一旦有耗时操作(如大数据计算、死循环等),会卡住主线程,导致页面卡顿或无响应。 - **无法利用多核 CPU**:在默认模式下,不能并行计算,浪费了现代多核处理器的能力。 * * * ### JavaScript 如何实现高并发与多线程? 虽然 JS 是单线程执行模型,但通过浏览器或 Node.js 提供的机制,我们可以实现“**伪并发**”或“多线程模拟”,主要方式如下: #### ✅ 异步操作(等加载时先干别的) - **原理**:任务被挂起,等待资源时让出主线程,通过事件队列机制在任务完成后重新调度执行。 - **常用方式**: - `setTimeout` / `setInterval` - `Promise` - `async/await` - Ajax / Fetch API #### ✅ Web Worker(开小号偷偷干活) - 开启一个独立的线程运行 JS 脚本,不影响主线程。 - 适用于大计算任务、离线数据预处理等。 - 与主线程通信使用 `postMessage()` / `onmessage` ```js // main.js const worker = new Worker("worker.js") worker.postMessage("开始计算") worker.onmessage = (e) => { console.log("子线程结果:", e.data) } ``` ```js // worker.js onmessage = function (e) { // 执行密集任务 let sum = 0 for (let i = 0; i < 1e8; i++) sum += i postMessage(sum) } ``` #### ✅ Node.js 中的 Worker Threads - 使用 `worker_threads` 模块在后端实现多线程能力,适合 CPU 密集型场景。 #### ✅ 任务拆碎(把大活切成小碎活穿插着做) - 利用 `requestIdleCallback`、`setTimeout` 分片处理数据,减少卡顿。 * * * ### 异步与同步的区别 #### 同步(Synchronous) - 执行顺序严格,必须等待上一个任务完成后才能执行下一个。 - 阻塞主线程。 ```js console.log("A") document.querySelector("button").click() // 阻塞直到点击 console.log("B") ``` #### 异步(Asynchronous) - 后台处理任务,不阻塞主线程,通过回调或事件通知结果。 ```js console.log("A") setTimeout(() => console.log("B"), 1000) console.log("C") // 输出顺序:A -> C -> B ``` * * * ### Promise、async 和 await 的理解与使用 #### Promise - 用于封装一个异步操作,避免回调地狱。 - 有三种状态:`pending`(等待中)、`fulfilled`(已完成)、`rejected`(已拒绝) - 通过 `.then()` / `.catch()` 链式处理结果。 ```js function fetchData() { return new Promise((resolve, reject) => { setTimeout(() => { const success = true success ? resolve("数据加载成功") : reject("失败") }, 1000) }) } fetchData() .then((data) => console.log(data)) .catch((err) => console.error(err)) ``` #### async/await - 是 `Promise` 的语法糖,让异步代码写起来像同步代码。 - 只能在 `async` 函数中使用。 - 使用 `try/catch` 更方便地处理异常。 ```js async function getData() { try { const data = await fetchData() console.log("结果:", data) } catch (err) { console.error("出错了:", err) } } getData() ``` * * * ### 总结 | 技术/特性 | 描述 | | --- | --- | | 单线程模型 | JS 默认仅一个主线程,任务顺序执行 | | 异步操作 | 不阻塞主线程,通过事件队列执行回调 | | Web Worker | 浏览器中模拟多线程,适合重任务 | | Node WorkerThreads | 后端的多线程计算方案 | | 任务拆分 | 将大任务拆成小块,分帧执行减轻压力 | | Promise | 管理异步逻辑,避免回调地狱 | | async/await | 让异步代码更像同步,提升可读性 | > > > 总之,单线程就像收银台只有一个店员,但现代网页用各种办法让这个店员手脚麻利到飞起。