1. 单线程与多线程
单线程:
- JavaScript 是一种单线程语言,这意味着它一次只能执行一项任务。这是因为 JavaScript 的执行环境(如浏览器或 Node.js)只有一个主线程来处理代码。
- 优点:避免了竞争条件和并发问题,因为一次只有一个任务在执行。
- 缺点:如果一个任务耗时过长,其他任务就必须等待,可能导致用户界面冻结。
多线程:
- 多线程意味着程序有多个线程可以同时执行任务。
- JavaScript 本身是单线程的,但可以通过Web Workers(浏览器)或Worker Threads(Node.js)实现一定程度的多线程处理。
关系:
- JavaScript 核心运行环境是单线程的,但可以通过 Web Workers 等方式实现多线程。单线程一次只能执行一个任务,多线程则可以并行执行多个任务。
2. 同步与异步
同步:
-
同步代码按顺序执行,当前任务必须完成,才能开始下一个任务。同步任务会阻塞其他任务的执行。
示例:
console.log('任务 1'); console.log('任务 2'); console.log('任务 3');
输出顺序是
任务 1
->任务 2
->任务 3
,它们是按顺序执行的。
异步:
-
异步代码允许 JavaScript 在处理某些任务时不阻塞主线程。
-
常见的异步操作包括:
setTimeout
/setInterval
- 网络请求(如
fetch()
) - 文件 I/O(Node.js)
示例:
console.log('任务 1'); setTimeout(() => { console.log('异步任务'); }, 1000); console.log('任务 2');
输出顺序是
任务 1
->任务 2
->异步任务
,异步任务会在 1 秒后执行。
关系:
- 同步任务按顺序执行,阻塞主线程,而异步任务允许在等待某个任务完成时执行其他任务,避免主线程被阻塞。
3. 并发与并行
并发:
-
并发是指即使在单线程环境中,通过异步编程,JavaScript 也可以管理多个任务。尽管这些任务不会在同一时刻同时执行,但它们可以“并发”进行。
并发示例:
setTimeout(() => console.log('异步任务 1'), 1000); setTimeout(() => console.log('异步任务 2'), 500); console.log('同步任务');
输出顺序是
同步任务
->异步任务 2
->异步任务 1
。异步任务之间通过事件循环机制进行调度。
并行:
-
并行指多个任务在同一时刻同时执行,这通常需要多线程支持。JavaScript 可以通过Web Workers实现并行。
并行示例(通过 Web Workers):
const worker = new Worker('worker.js'); worker.postMessage('开始计算'); worker.onmessage = function(event) { console.log('从 worker 接收到消息:', event.data); };
关系:
- 并发是在单线程环境中通过异步任务管理多个任务,而并行是通过多线程同时执行多个任务。
4. JavaScript 的事件循环(Event Loop)
JavaScript 的事件循环机制是管理异步和并发任务的核心。它允许 JavaScript 以非阻塞的方式执行任务,尽管 JavaScript 是单线程的。
事件循环的工作流程:
- 执行同步任务,直到任务队列为空。
- 检查异步任务队列,执行回调或处理已完成的异步任务。
- 循环执行以上步骤。
示例:
console.log('任务 1');
setTimeout(() => console.log('异步任务'), 1000);
console.log('任务 2');
输出顺序:任务 1
-> 任务 2
-> 异步任务
。
总结关系
- JavaScript 是单线程的,但通过异步编程和事件循环,它可以并发执行任务。
- 同步任务会阻塞主线程,而异步任务可以在不阻塞的情况下执行。
- JavaScript 可以通过Web Workers等方式实现多线程,实现并行处理多个任务。
深入理解异步–减少的是等待时间而不是执行时间,任务最终要放在主线程执行
- 异步代码允许 JavaScript 在处理某些任务时不阻塞主线程。通常用于
setTimeout
、setInterval
、网络请求、文件操作等不会立即返回结果的任务。 - 怎么减少的?在浏览器的配合下,异步操作可以减少等待时间:在执行异步任务(如网络请求或文件读取)时,JavaScript 发出请求后,不会等待它的完成,而是继续执行后续代码。当异步操作完成(例如定时器到期、服务器响应数据回来)时,浏览器会将其回调函数加入到任务队列,等待主线程空闲后再执行这些回调。这样,JavaScript 不会因为等待外部资源(如服务器响应)而浪费时间,确保主线程能够保持高效执行。
- 如果将计算密集型任务设置为异步任务,由于计算任务仍然会在主线程上执行,它依然会阻塞主线程。因此,异步任务的作用并不是减少计算时间,而是避免在等待外部资源时阻塞主线程。
怎么分配任务:
- 需要浪费时间等待的任务,例如定时器或网络请求等。建议使用 异步,它的优势在于减少等待时间,使得主线程不会因等待外部资源的响应而闲置;
- 计算量大的任务,例如复杂的循环或递归。建议使用Web Worker,它可以在独立的线程中执行计算密集型任务,避免阻塞主线程,从而保持应用程序的流畅性。