拆解 Promise: 异步编程解决

1 阅读3分钟

摘要:  作为一个编程新手,面对 JavaScript 中的异步操作,你是否曾被层层嵌套的回调函数搞得晕头转向?Promise 是 ES6 中引入的异步编程解决方案,它不仅解决了“回调地狱”问题,还让代码逻辑更加清晰。


一、为什么要学 Promise?告别“回调地狱”

在 JavaScript 的世界里,异步操作(如定时器、网络请求、文件读取)是家常便饭。在 ES6 之前,我们主要依靠回调函数(Callback) 来处理异步逻辑。

假设我们需要依次执行三个异步任务(A -> B -> C),传统的写法可能是这样的:

// 伪代码:回调地狱
doAsyncTaskA(function(resultA) {
    doAsyncTaskB(resultA, function(resultB) {
        doAsyncTaskC(resultB, function(resultC) {
            console.log('所有任务完成:', resultC);
        });
    });
});

这种层层嵌套的写法被称为 “回调地狱”(Callback Hell) 。随着业务逻辑变复杂,代码会变得难以阅读和维护。

Promise 的出现就是为了解决这个问题。

什么是 Promise?
简单来说,Promise 是一个对象,代表一个现在、将来完成或可能永远不完成的异步操作。它就像一张“契约”:

  1. 状态不可逆:Promise 有三种状态:

    • pending(等待中)
    • fulfilled(成功/已兑现)
    • rejected(失败/已拒绝)
    • 状态一旦从 pending 变为 fulfilled 或 rejected,就永远凝固,不会再改变。
  2. 立即执行:创建 Promise 实例时,传入的执行函数(Executor)会立即执行


二、Promise 的核心用法(ES6 标准)

Promise 是一个构造函数,接收一个函数作为参数,该函数包含两个参数:resolve 和 reject

1. 基础结构

const promise = new Promise((resolve, reject) => {
// 异步操作代码
    setTimeout(() => {
        const random = Math.random();
        if (random > 0.5) {
            resolve('成功!数据是:' + random); // 调用 resolve,状态变为 fulfilled
        } else {
            reject('失败!数字太小'); // 调用 reject,状态变为 rejected
        }
    }, 1000);
});

2. then 方法:链式调用的灵魂
then 方法用于指定状态改变后的回调函数。它有两个参数,分别对应成功和失败的回调。

  • 链式调用then 方法返回的是一个新的 Promise 对象,这使得我们可以进行链式调用,彻底摆脱嵌套。
promise
    .then(
        (data) => { console.log('成功:', data); return data; }, // 成功回调
        (err) => { console.log('失败:', err); } // 失败回调(通常建议用 catch 代替)
     )  
     .then((data) => {
        // 这里的 data 是上一个 then 返回的结果
        console.log('上一步的结果传递过来了:', data);
    });

3. catch 方法:错误捕获
catch 专门用于处理错误,相当于 then(null, rejection)。它还有一个重要功能:捕获前面任何 then 中抛出的异常(类似于 try...catch)。

promise
    .then(data => {
        console.log(someUndefinedVar); // 这里会报错
    })
    .catch(err => {
        console.log('捕获到错误了:', err); // 即使代码出错,也能被捕获,不会卡死
    });

4. 静态方法:批量处理异步
Promise 还提供了一些静态方法来处理多个异步操作:

  • Promise.all([...])谁跑得慢,以谁为准。

    • 接收一个 Promise 数组。
    • 全成功才成功:所有任务都成功完成,结果按顺序组成数组。
    • 一失败即失败:只要有一个失败,就立即进入 reject。
    function createTask(name, delay, success = true) {
       return new Promise((resolve, reject) => {
         setTimeout(() => {
           if (success) {
             resolve(`✅ ${name} 完成,耗时 ${delay}ms`);
           } else {
             reject(`❌ ${name} 失败,耗时 ${delay}ms`);
           } 
         }, delay);
       });
     }
    
    
    
    
    
    
  • Promise.race([...])谁跑得快,以谁为准。

    • 接收一个 Promise 数组。
    • 一决生死:哪个 Promise 最先改变状态(无论是成功还是失败),race 的状态就跟着它走。
    • 场景:设置请求超时。
        function timeout(ms) { 
           return new Promise((_, reject) => { 
               setTimeout(() => {
                   reject(`⏰ 请求超时,超过 ${ms}ms`);
                 }, ms);
              });
          }