首页/文章/javascript

JavaScript异步编程中的 Promise 机制解析

2025-07-23
18687 分钟
...

一、了解进程与线程

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

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

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

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

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

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

function date() {
  setTimeout(() => {
    console.log("相亲成功");
    return true;
  }, 1000);
}

function marry() {
  console.log("结婚");
}

date();
marry();

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


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

1. 什么是回调函数?

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

function fetchData(callback) {
    console.log("开始获取数据...");
    setTimeout(() => {
        const data = "这里是从服务器获取的数据";
        callback(data); 
    }, 2000); 
}

function handleData(data) {
    console.log("获取的数据:", data);
}


fetchData(handleData);

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

2. 回调函数的问题

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

API1(() => {
  API2(() => {
    API3(() => {
      
    });
  });
});

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


四、Promise:异步编程的救星

1. Promise的基本概念

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

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

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

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

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

function fetchData() {
    return new Promise((resolve, reject) => {
        console.log("开始获取数据...");
        setTimeout(() => {
            const data = "这里是从服务器获取的数据";
            resolve(data); 
            
        }, 2000);
    });
}





fetchData() 
    .then((data) => {
        console.log("获取的数据:", data);
    })
    .catch((error) => {
        console.error("发生错误:", error);
    });

在这个实例中,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。

async function example() { 
  return 42; 
}

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

async function foo() {
  const result = await somePromise; 
  return result;
}

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


fetchData()
  .then(data => processData(data))
  .then(result => displayResult(result))
  .catch(error => handleError(error));


async function handleData() {
  try {
    const data = await fetchData();
    const result = await processData(data);
    displayResult(result);
  } catch (error) {
    handleError(error);
  }
}

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
javascript2025-09-18
JavaScript 的多线程能力:Worker
如果你写过一些计算量稍大的 JavaScript 代码,比如图像处理、大量数据排序或者复杂的算法,你几乎肯定遇到过浏览器“卡死”的现象。点击页面没反应,动画也停了,就像整个世界都静止了。 这就是主线程被阻塞的典型后果。因为主线程既要负责执行 JavaScript,又要负责渲染页面、响应用户操作,一旦它被繁重的计算任务占满,就无暇顾及其他,用户体验便直线下降。 这个问题的根源,正是“主线程是单线程的”。那么,如何解决呢? 答案很简单:把这...
学习面试
javascript2025-09-10
手把手教你实现一个EventEmitter
你是不是也遇到过这种情况:代码里各种事件监听和触发,回调函数套了一层又一层,最后自己都理不清哪个事件先触发、哪个后触发了? 别担心,今天我就带你从零开始实现一个自己的事件触发器(EventEmitter),让你彻底掌握事件驱动的精髓,代码清晰度直接提升200%! 为什么要懂EventEmitter? 先说说为什么我们要关心这个。Node.js的核心就是事件驱动,比如文件读写、网络请求,都是通过事件来处理的。不会EventEmitter,...
学习面试
javascript2025-08-09
Web Worker 多线程魔法
你是否曾遇到过网页因为复杂计算而卡顿的情况?今天,让我们一起探索Web Worker的神奇世界,看看如何用多线程技术解决性能瓶颈,并实现流畅的图片压缩功能! 为什么需要Web Worker? 想象一下,你在一个繁忙的餐厅里,只有一位服务员(主线程)负责点餐、上菜、结账所有工作。当客人突然暴增时,这位服务员就会手忙脚乱,其他客人只能干等着… 这就是JavaScript的现状—— 单线程 !所有任务都在一个线程中排队执行,一旦遇到复杂计算(...
学习面试
javascript2025-08-07
🌐AST抽象语法树
AST 是前端开发中一个非常重要的概念,它就像是代码的“编译器”,能够帮助我们理解和操作代码。本文将深入探讨 AST 的原理和应用,带你从零开始了解 AST。 一、什么是 AST? AST(抽象语法树)是一种树形结构,用于表示程序代码的语法结构。它将代码分解为一系列的节点,每个节点代表代码中的一个元素,如变量、函数、表达式等。AST 是编译器和解释器的基础,也是代码分析和转换的关键工具。 1\. AST 的结构 AST 的结构通常包括以...
学习面试
javascript2025-08-05
打字机效果是如何实现的
分析效果 使用 AI 对话的时候,文字是一个字一个字蹦出来的?它不是等所有内容生成完毕后一次性展示,而是以一种流畅、实时的“打字机”效果呈现,这种体验的背后,隐藏着一种高效的Web技术。它就是 服务器推送事件(Server Sent Events,简称 SSE) 什么是 SSE 从根本上说,SSE 是一种允许服务器 单向 、 持续 地向客户端(浏览器)推送数据的Web技术。 可以把它想象成一个 电台广播 : 服务器 是广播电台,持续不断...
学习面试
javascript2025-08-04
Promise 从原理到实现
前言 是 异步编程的核心概念之一,它为我们提供了一种更优雅的方式来处理异步操作。 本文将全面剖析 的工作原理,并逐步实现一个符合 规范的 。 正文 一、Promise 基础概念 代表一个异步操作的最终完成(或失败)及其结果值。它有 种状态: 1. pending : ,既不是成功,也不是失败状态 2. fulfilled :意味着操作 完成 3. rejected :意味着操作 状态一旦改变就不能再变,只能从 变为 ,或从 变为 。 二...
学习面试

评论

请登录后发表评论

去登录
加载评论中...

目录