JavaScript Promise完整指南

一、Promise 基础知识

1. Promise 是什么?

  • 定义:Promise 是 JavaScript 中用于处理异步操作的对象,通过链式调用避免回调地狱(Callback Hell)。

  • 核心思想 :将异步操作的结果封装成一个对象,通过 .then().catch() 处理成功或失败的结果。

  • 状态

    • Pending(等待态) :初始状态,异步操作未完成。
    • Fulfilled(成功态) :异步操作成功,返回结果。
    • Rejected(失败态) :异步操作失败,返回错误。

2. 基础用法:创建和使用 Promise

示例 1:简单异步操作

typescript 复制代码
// 创建一个 Promise,模拟异步操作(如网络请求)
const fetchData = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true; // 模拟成功或失败
    if (success) {
      resolve("数据加载成功!");
    } else {
      reject("数据加载失败!");
    }
  }, 1000);
});

// 使用 then 和 catch 处理结果
fetchData
  .then(result => {
    console.log("成功:", result); // 输出: 成功: 数据加载成功!
  })
  .catch(error => {
    console.error("失败:", error);
  });

解析

  • new Promise(executor) :创建一个 Promise,executor 是立即执行的函数,包含异步逻辑。
  • resolve(value) :异步操作成功时调用,将 Promise 状态从 pending 改为 fulfilled
  • reject(error) :异步操作失败时调用,将状态改为 rejected
  • .then() :处理成功的结果。
  • .catch() :处理错误。

示例 2:链式调用处理多个步骤

javascript 复制代码
// 第一步:获取用户数据
function getUserData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ id: 1, name: "Alice" });
    }, 1000);
  });
}

// 第二步:根据用户ID获取订单数据
function getUserOrders(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve([{ orderId: 101, total: 99.99 }]);
    }, 500);
  });
}

// 链式调用
getUserData()
  .then(user => {
    console.log("用户信息:", user); // 输出: 用户信息: { id: 1, name: "Alice" }
    return getUserOrders(user.id); // 返回新的 Promise
  })
  .then(orders => {
    console.log("订单列表:", orders); // 输出: 订单列表: [{ orderId: 101, total: 99.99 }]
  })
  .catch(error => {
    console.error("发生错误:", error);
  });

解析

  • 链式调用 :每个 .then() 返回一个新的 Promise,允许连续执行异步操作。
  • 中间值传递 :第一个 .then() 返回的值会作为第二个 .then() 的参数。
  • 错误传递 :链中的任何错误都会被 .catch() 捕获。

示例 3:错误处理与恢复

javascript 复制代码
// 模拟一个会失败的异步操作
function fetchDataWithError() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("网络错误!");
    }, 1000);
  });
}

// 错误处理与恢复
fetchDataWithError()
  .then(data => {
    console.log("成功:", data);
  })
  .catch(error => {
    console.error("错误:", error); // 输出: 错误: 网络错误!
    // 恢复逻辑:返回默认值
    return { data: "默认数据" };
  })
  .then(data => {
    console.log("恢复后的数据:", data); // 输出: 恢复后的数据: { data: "默认数据" }
  });

解析

  • .catch() :捕获错误后,可以通过返回一个值继续链式调用。
  • 错误恢复 :在 .catch() 中返回一个新值或新 Promise,后续 .then() 会接收到该值。

二、Promise 高级用法

示例 4:Promise.all() 并行执行多个请求

javascript 复制代码
// 模拟三个异步请求
function request1() {
  return new Promise(resolve => setTimeout(() => resolve("结果1"), 1000));
}
function request2() {
  return new Promise(resolve => setTimeout(() => resolve("结果2"), 500));
}
function request3() {
  return new Promise(resolve => setTimeout(() => resolve("结果3"), 800));
}

// 并行执行所有请求
Promise.all([request1(), request2(), request3()])
  .then(results => {
    console.log("所有请求成功:", results); // 输出: ["结果1", "结果2", "结果3"]
  })
  .catch(error => {
    console.error("任一请求失败:", error);
  });

解析

  • Promise.all() :接收一个 Promise 数组,所有成功时返回结果数组,任一失败则直接拒绝。
  • 并行执行:所有异步操作同时启动,但按顺序返回结果。

示例 5:Promise.race() 竞速模式

javascript 复制代码
// 模拟两个异步请求
function fastRequest() {
  return new Promise(resolve => setTimeout(() => resolve("快速请求"), 500));
}
function slowRequest() {
  return new Promise(resolve => setTimeout(() => resolve("慢速请求"), 1000));
}

// 竞速:返回最快完成的请求
Promise.race([fastRequest(), slowRequest()])
  .then(result => {
    console.log("最快完成:", result); // 输出: 最快完成: 快速请求
  })
  .catch(error => {
    console.error("最快失败:", error);
  });

解析

  • Promise.race() :返回第一个完成的 Promise(无论成功或失败)。
  • 应用场景:适合需要"最先完成即成功"的场景(如超时控制)。

示例 6:设置异步操作超时

javascript 复制代码
// 模拟一个可能超时的请求
function fetchWithTimeout(promise, timeout = 3000) {
  return Promise.race([
    promise,
    new Promise((_, reject) =>
      setTimeout(() => reject("请求超时"), timeout)
    )
  ]);
}

// 使用超时控制
fetchWithTimeout(
  new Promise(resolve => setTimeout(() => resolve("数据"), 4000)),
  2000
)
  .then(data => {
    console.log("成功:", data);
  })
  .catch(error => {
    console.error("错误:", error); // 输出: 错误: 请求超时
  });

解析

  • Promise.race() :与超时 Promise 竞速,若原请求未在指定时间内完成,则触发超时。
  • 应用场景:防止异步操作长时间阻塞用户界面。

三、Promise 与 Async/Await

1. 基本用法

  • Async/Await 是 Promise 的语法糖,让异步代码更接近同步写法。
javascript 复制代码
// 模拟异步操作
function fetchData() {
  return new Promise(resolve => setTimeout(() => resolve("数据"), 1000));
}

// 使用 async/await
async function run() {
  try {
    const data = await fetchData();
    console.log("成功:", data); // 输出: 成功: 数据
  } catch (error) {
    console.error("错误:", error);
  }
}

run();

解析

  • async 函数 :返回一个 Promise,内部可用 await 暂停执行直到 Promise 完成。
  • 同步风格代码:异步逻辑看起来像同步代码,提高可读性。

2. 并发控制

  • Promise.all() + await:并行执行多个异步操作。
javascript 复制代码
async function runAll() {
  try {
    const [result1, result2] = await Promise.all([
      new Promise(resolve => setTimeout(() => resolve("A"), 1000)),
      new Promise(resolve => setTimeout(() => resolve("B"), 500))
    ]);
    console.log("结果:", result1, result2); // 输出: 结果: A B
  } catch (error) {
    console.error("错误:", error);
  }
}

runAll();

3. 实际应用场景:表单提交与数据验证

javascript 复制代码
// 模拟异步验证用户名是否已存在
function checkUsername(username) {
  return new Promise(resolve => {
    setTimeout(() => {
      const exists = username === "admin";
      resolve(!exists);
    }, 500);
  });
}

// 表单提交逻辑
async function submitForm(username, password) {
  try {
    if (username.length < 4) {
      throw new Error("用户名至少4个字符");
    }
    const isAvailable = await checkUsername(username);
    if (!isAvailable) {
      throw new Error("用户名已存在");
    }
    if (password.length < 6) {
      throw new Error("密码至少6个字符");
    }
    console.log("表单提交成功!");
  } catch (error) {
    console.error("表单提交失败:", error.message);
  }
}

// 调用示例
submitForm("user123", "123456"); // 输出: 表单提交成功!

解析

  • 异步验证 :结合 async/await 和 Promise,实现表单字段的异步验证。
  • 错误处理 :通过 try/catch 捕获并处理验证错误。

四. 手写 Promise 核心逻辑

简易版

ini 复制代码
class MyPromise {
  constructor(executor) {
    this.state = "pending";
    this.value = undefined;
    this.reason = undefined;

    const resolve = (value) => {
      if (this.state === "pending") {
        this.state = "fulfilled";
        this.value = value;
      }
    };

    const reject = (reason) => {
      if (this.state === "pending") {
        this.state = "rejected";
        this.reason = reason;
      }
    };

    executor(resolve, reject);
  }

  then(onFulfilled, onRejected) {
    if (this.state === "fulfilled") {
      onFulfilled(this.value);
    } else if (this.state === "rejected") {
      onRejected(this.reason);
    }
  }
}

// 使用示例
new MyPromise((resolve, reject) => {
  setTimeout(() => resolve("自定义Promise"), 1000);
})
  .then(result => {
    console.log(result); // 输出: 自定义Promise
  })
  .catch(error => {
    console.error(error);
  });

解析

  • 手写 Promise :模拟 Promise 的核心逻辑,包括状态管理、resolve/reject 调用和 .then() 处理。
  • 学习目标:理解 Promise 的底层原理,如状态不可逆、链式调用等。

完整版

ini 复制代码
class MyPromise {
  constructor(executor) {
    this.state = "pending";
    this.value = undefined;
    this.reason = undefined;

    // 存储成功/失败回调
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (this.state === "pending") {
        this.state = "fulfilled";
        this.value = value;
        // 异步执行所有成功回调
        this.onFulfilledCallbacks.forEach(fn => fn());
      }
    };

    const reject = (reason) => {
      if (this.state === "pending") {
        this.state = "rejected";
        this.reason = reason;
        // 异步执行所有失败回调
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };

    executor(resolve, reject);
  }

  then(onFulfilled, onRejected) {
    // 默认值处理
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
    onRejected = typeof onRejected === "function" ? onRejected : error => { throw error; };

    // 返回新 Promise 支持链式调用
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.state === "fulfilled") {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }

      if (this.state === "rejected") {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }

      if (this.state === "pending") {
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });

        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });

    return promise2;
  }

  catch(onRejected) {
    return this.then(undefined, onRejected);
  }
}

// 处理 then 返回值与新 Promise 的关系
function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError("Chaining cycle detected for promise"));
  }

  let called = false;
  if (x instanceof MyPromise) {
    if (x.state === "pending") {
      x.then(value => {
        resolvePromise(promise2, value, resolve, reject);
      }, reject);
    } else {
      x.then(resolve, reject);
    }
    return;
  }

  if (x !== null && (typeof x === "object" || typeof x === "function")) {
    try {
      const then = x.then;
      if (typeof then === "function") {
        then.call(x, value => {
          if (called) return;
          called = true;
          resolvePromise(promise2, value, resolve, reject);
        }, error => {
          if (called) return;
          called = true;
          reject(error);
        });
      } else {
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}

修复后的使用示例

typescript 复制代码
new MyPromise((resolve, reject) => {
  setTimeout(() => resolve("自定义Promise"), 1000);
})
  .then(result => {
    console.log("成功:", result); // 输出: 成功: 自定义Promise
    return "下一步"; // 返回新值
  })
  .then(result => {
    console.log("下一步结果:", result); // 输出: 下一步结果: 下一步
  })
  .catch(error => {
    console.error("失败:", error); // 现在可以正常调用 catch
  });

修复点解析

1. 添加 catch 方法

  • 问题 :原代码没有定义 catch 方法,导致 .catch() 报错。

  • 修复

    kotlin 复制代码
    javascript
    深色版本
    catch(onRejected) {
      return this.then(undefined, onRejected);
    }
    • catchthen(null, onRejected) 的语法糖。

2. 链式调用支持

  • 问题 :原 then 没有返回新 Promise,导致 .then().catch() 无法链式调用。

  • 修复

    • then 返回一个新的 MyPromise 实例。
    • 使用 resolvePromise 函数处理回调的返回值,确保能正确传递到下一个 then

3. 异步回调处理

  • 问题 :原代码未处理 pending 状态下的回调存储。

  • 修复

    • 增加 onFulfilledCallbacksonRejectedCallbacks 数组,用于存储回调。
    • resolvereject 被调用时,异步执行这些回调。

4. 处理异步操作

  • 问题 :原代码未处理异步操作(如 setTimeout)导致 then 回调未触发。

  • 修复

    • then 中使用 setTimeout 延迟执行回调,确保在事件循环中执行。
    • 通过 resolvePromise 处理返回值(包括 Promise 对象)。

总结

通过上述示例,你可以掌握 Promise 的以下核心技能:

  1. 基础用法:创建和处理 Promise。
  2. 链式调用:连续执行异步操作。
  3. 错误处理:捕获和恢复错误。
  4. 静态方法Promise.allPromise.race 的使用。
  5. 超时控制:结合竞速模式实现超时逻辑。
  6. Async/Await:简化异步代码。
  7. 实际应用:如表单验证、并行请求等。
  8. 底层原理:手写简化版 Promise。

这些技能将帮助你在工作中高效处理异步操作,并在面试中从容应对 Promise 相关问题。

相关推荐
JSON_L14 分钟前
Vue 电影导航组件
前端·javascript·vue.js
xptwop2 小时前
05-ES6
前端·javascript·es6
Heo2 小时前
调用通义千问大模型实现流式对话
前端·javascript·后端
前端小巷子3 小时前
深入 npm 模块安装机制
前端·javascript·面试
深职第一突破口喜羊羊4 小时前
记一次electron开发插件市场遇到的问题
javascript·electron
cypking4 小时前
electron中IPC 渲染进程与主进程通信方法解析
前端·javascript·electron
西陵4 小时前
Nx带来极致的前端开发体验——借助playground开发提效
前端·javascript·架构
江城开朗的豌豆4 小时前
Element UI动态组件样式修改小妙招,轻松拿捏!
前端·javascript·vue.js
float_六七5 小时前
JavaScript:现代Web开发的核心动力
开发语言·前端·javascript
zhaoyang03015 小时前
vue3笔记(2)自用
前端·javascript·笔记