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 相关问题。

相关推荐
雲墨款哥1 小时前
JS算法练习-Day10-判断单调数列
前端·javascript·算法
JuneXcy1 小时前
11.web api 2
前端·javascript·html
zYear1 小时前
Elpis 全栈应用框架-- 总结
前端·javascript
Juchecar2 小时前
分析:将现代开源浏览器的JavaScript引擎更换为Python的可行性与操作
前端·javascript·python
伍哥的传说3 小时前
Vue 3.5重磅更新:响应式Props解构,让组件开发更简洁高效
前端·javascript·vue.js·defineprops·vue 3.5·响应式props解构·vue.js新特性
德育处主任4 小时前
p5.js 3D 形状 "预制工厂"——buildGeometry ()
前端·javascript·canvas
Mintopia4 小时前
React 牵手 Ollama:本地 AI 服务对接实战指南
前端·javascript·aigc
Mintopia4 小时前
Next.js 全栈开发基础:在 pages/api/*.ts 中创建接口的艺术
前端·javascript·next.js
xvmingjiang4 小时前
Element Plus 中 el-input 限制为数值输入的方法
前端·javascript·vue.js
狂炫一碗大米饭5 小时前
事件委托的深层逻辑:当冒泡不够时⁉️
javascript·面试