深入理解JavaScript的进程、线程与Event-Loop

1,367 阅读3分钟

前言

在Web开发中,我们经常听到关于JavaScript是单线程的说法。但是,这种单线程的执行模型又是如何在浏览器中工作的呢?本文将深入探讨JavaScript的进程、线程以及Event-Loop,以帮助我们更好地理解和利用JavaScript的异步编程特性。

进程与线程

进程是指CPU在运行指令和保存上下文所需的时间,是程序的一次执行过程。而线程是进程中更小的单位,指的是一段指令执行所需的时间。在浏览器中,一个Tab页可以看作一个进程,而渲染线程、HTTP请求线程和JS引擎线程则是协同工作的线程。

// 代码示例 - 创建一个新的进程(Tab页)
// 由于JavaScript无法直接操作进程和线程,这里用伪代码表示
const newTab = createNewTab();
newTab.createThread("渲染线程");
newTab.createThread("HTTP请求线程");
newTab.createThread("JS引擎线程");

JavaScript的单线程特性

JavaScript是单线程的,这意味着它一次只能执行一个任务。这种设计带来了一些优点,比如节约内存和减少上下文切换的时间。

// 代码示例 - 单线程执行
function task1() {
  console.log("Task 1");
}

function task2() {
  console.log("Task 2");
}

task1();
task2(); // 只有当task1执行完毕后,才会执行task2

异步任务

异步任务分为宏任务和微任务,通过事件循环机制来执行。

  • 宏任务(macrotask):script,setTimeout,setInterval,setImmediate, I/O,UI-rendering

  • 微任务(microtask):promise.then(), MutationObserver, process.nextTick()

Event-Loop的运行机制

Event-Loop是JavaScript异步执行的基础,其运行机制可以简述为:

  1. 执行同步代码,属于宏任务。
  2. 当执行栈为空时,查询是否有异步任务需要执行。
  3. 执行微任务队列中的任务。
  4. 如果需要,渲染页面。
  5. 执行宏任务队列中的任务,开始下一轮Event-Loop。

接下来用一个片段来举例:

console.log(1);
setTimeout(() => {
    console.log(2);
    new Promise((resolve) => {
        console.log(4);
        resolve()
        setTimeout(() => {
            console.log(6);
        })
    }).then(() => {
        console.log(5);
    })
},1000)
console.log(3);

// 整个事件循环的打印顺序是  1 3 2 4 5 6

按照代码的执行顺序先打印1,setTimeout属于宏任务放入宏任务队列,再继续打印3,至此第一轮事件循环结束,然后把setTimeout拿下来开启第二轮事件循环,打印2,promise是同步代码,只有promise.then才是异步属于微任务,然后打印4,setTimeout进入宏任务队列,then进入微任务队列,至此setTimeout里的同步代码执行完毕,接下来执行里面的微任务,打印5,然后第二轮事件循环结束,再从宏任务队列里拿出setTimeout开启第三轮,打印6

微信图片_20240120193938.jpg

结语

深入理解JavaScript的进程、线程与Event-Loop是提高开发效率和代码性能的关键。通过灵活应用异步编程、任务队列和Event-Loop的原理,我们可以更好地处理复杂的业务逻辑和提升用户体验。在实际开发中,合理利用JavaScript的单线程特性将成为我们编写高效、流畅代码的利器。