一、了解进程与线程

在不同场景中,进程都可以用来描述该场景中的一个效果,线程进程里面的一个更小的单位,通常多个线程配合工作构成一个进程

  • 进程:操作系统资源分配的基本单位,每个进程拥有独立的内存空间和资源。一个进程可以有多个线程。比如在Windows系统中,一个运行的xx.exe就是一个进程。它是一个运行中的程序实例,负责完成特定任务。进程之间相互独立,切换时需要较大的系统开销。
  • 线程:进程中的执行单元,CPU调度的基本单位。线程共享进程的内存和资源,但每个线程有自己的栈和程序计数器。线程切换的开销较小,因此被称为轻量级进程。

二、v8 引擎的异步执行机制

js 默认是单线程的语言,因为 js 设定是为了做浏览器的脚本语言,尽量少的开销用户设备的性能。因为 js 的这种执行规则,导致我们在开发过程中时而会出现代码异步的情况。

  • 同步代码:按顺序执行,阻塞后续代码直至完成。
  • 异步代码:被挂起并放入任务队列,待同步代码执行完毕后进入微任务/宏任务队列。

v8运行一份js代码,会创建一个进程,从上往下执行代码,遇到同步代码就直接执行,遇到异步代码就跳过,先去执行后面的同步代码,等到后面的同步代码全部执行完毕后,再回过头执行异步代码。
以相亲函数案例说明传统模式的缺陷:

 1function date() {
 2  setTimeout(() => {
 3    console.log("相亲成功");
 4    return true;
 5  }, 1000);
 6}
 7
 8function marry() {
 9  console.log("结婚");
10}
11
12date();
13marry();

上述代码执行结果为结婚 相亲成功,其根本原因是v8引擎跳过异步代码继续执行后续同步代码,导致逻辑顺序错乱。


三、回调函数:异步编程的基础

1. 什么是回调函数?

回调函数是指将一个函数作为参数传递给另一个函数,并在外部函数内部调用这个传入的函数。在异步编程中,回调函数通常用于处理异步操作完成后的结果。

 1function fetchData(callback) {
 2    console.log("开始获取数据...");
 3    setTimeout(() => {
 4        const data = "这里是从服务器获取的数据";
 5        callback(data); 
 6    }, 2000); 
 7}
 8
 9function handleData(data) {
10    console.log("获取的数据:", data);
11}
12
13
14fetchData(handleData);

在这个示例中,fetchData函数模拟了从服务器获取数据的过程,使用setTimeout模拟网络延迟。callback参数是我们传入的处理函数,当数据准备好后,通过callback(data)调用这个处理逻辑。

2. 回调函数的问题

早期浏览器广泛支持回调函数,适合简单的异步操作。但当嵌套过深时,代码的可读性差,维护困难,排查问题困难,形成回调地狱
回调地狱:多层嵌套导致代码可读性灾难。

 1API1(() => {
 2  API2(() => {
 3    API3(() => {
 4      
 5    });
 6  });
 7});

代码结构随异步层级增加呈”括号金字塔”形态,严重降低维护性。


四、Promise:异步编程的救星

1. Promise的基本概念

promise 是 es6 新增的一个语法,用来解决回调地狱的问题。promise 是一个构造函数,用来封装一个异步操作,并且可以获取到异步操作的结果。
promise 有三种状态: 待定态(pending)、兑现态(fulfilled)、拒绝态(rejected)。

  • pending:初始状态,既不是成功,也不是失败;

  • fulfilled​:操作成功完成,通过 resolve 方法传递结果。

  • rejected​:操作失败,通过 reject 方法传递错误信息。

promise 状态的改变只有两种: 从待定态到兑现态或者从待定态到拒绝态。一旦状态变为 fulfilledrejected,就不会再变(不可逆性)。

 1function fetchData() {
 2    return new Promise((resolve, reject) => {
 3        console.log("开始获取数据...");
 4        setTimeout(() => {
 5            const data = "这里是从服务器获取的数据";
 6            resolve(data); 
 7            
 8        }, 2000);
 9    });
10}
11
12
13
14
15
16fetchData() 
17    .then((data) => {
18        console.log("获取的数据:", data);
19    })
20    .catch((error) => {
21        console.error("发生错误:", error);
22    });

在这个实例中,fetchData返回一个Promise对象。在Promise内部,我们仍然使用setTimeout模拟异步操作。数据成功时调用resolve(data),将结果传递给.then()中的方法;如果发生错误,则调用reject(error)并通过.catch()捕获。
Promise.then() 方法返回一个新的 Promise 实例,从而支持链式调用。如果回调函数返回一个值,新Promise将以该值为结果resolve

2. Promise的核心优势

特性传统回调Promise机制
代码结构嵌套层级加深链式调用(.then().then())
错误处理分散在各回调中统一错误捕获(.catch())
并行操作手动协调Promise.all()统一管理
状态追踪黑箱操作明确的pending/resolved状态

五、展望:从Promise到async/await

asyncawait本质上是生成器(Generator)与Promise的组合封装
async 函数​:用 async 声明的函数会自动返回一个 Promise 对象。无论函数内部返回的是否是Promise,都会被包装成 Promise。

 1async function example() { 
 2  return 42; 
 3}

await 表达式​:await 会暂停当前 async 函数的执行,等待后面的Promise完成。如果是非Promise值,会使用Promise.resolve()包装。

 1async function foo() {
 2  const result = await somePromise; 
 3  return result;
 4}

相比Promise链式调用,async/await使异步代码呈现同步代码的线性结构,消除了回调地狱问题。

 1
 2fetchData()
 3  .then(data => processData(data))
 4  .then(result => displayResult(result))
 5  .catch(error => handleError(error));
 6
 7
 8async function handleData() {
 9  try {
10    const data = await fetchData();
11    const result = await processData(data);
12    displayResult(result);
13  } catch (error) {
14    handleError(error);
15  }
16}
个人笔记记录 2021 ~ 2025