Promise

  1. Promise 概述

    Promise 是 JavaScript 中用于处理异步操作的对象,它代表了一个未来某个事件(通常是一个异步操作)最终完成或失败的状态。通过使用 Promise,可以优化回调函数的嵌套结构,使异步流程更加清晰和可维护,并提供了错误处理机制。


    1. 基本概念

    定义

    • Promise 是一个表示未来某个事件(通常是一个异步操作)最终完成或失败的对象。

    特点

    • 状态:一旦状态改变,就不会再变。Promise 对象的状态有以下三种:
      • Pending(待定):初始状态,既未完成也未失败。
      • Fulfilled(已完成):操作成功完成。
      • Rejected(已失败):操作失败。
    • 状态只能从 pending 变为 fulfilledrejected

    核心作用

    • 优化回调函数的嵌套结构:避免“回调地狱”问题。
    • 使异步流程更清晰和可维护:通过链式调用实现异步代码的顺序执行。
    • 提供错误处理机制:统一管理异步操作中的错误。

    2. Promise 的语法

    2.1 创建一个 Promise

    1
    2
    3
    4
    5
    6
    7
    const promise = new Promise((resolve, reject) => {
    if (/* 成功条件 */) {
    resolve("成功的结果");
    } else {
    reject("失败的原因");
    }
    });

    2.2 基本使用

    1
    2
    3
    4
    5
    6
    7
    promise
    .then(result => {
    console.log("成功:", result);
    })
    .catch(error => {
    console.error("失败:", error);
    });

    2.3 方法链

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    promise
    .then(result => {
    console.log("第一步成功:", result);
    return "下一步的结果";
    })
    .then(nextResult => {
    console.log("第二步成功:", nextResult);
    })
    .catch(error => {
    console.error("发生错误:", error);
    });

    3. Promise 的方法

    3.1 静态方法

    • **Promise.resolve(value)**:创建一个状态为已完成(fulfilled)的 Promise。

      1
      2
      const p = Promise.resolve(42);
      p.then(value => console.log(value)); // 输出: 42
    • **Promise.reject(reason)**:创建一个状态为已失败(rejected)的 Promise。

      1
      2
      const p = Promise.reject("错误信息");
      p.catch(error => console.log(error)); // 输出: 错误信息
    • **Promise.all(iterable)**:等待所有 Promise 完成,如果有一个失败,则返回失败。

      1
      2
      const promises = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
      Promise.all(promises).then(results => console.log(results)); // 输出: [1, 2, 3]
    • **Promise.allSettled(iterable)**:等待所有 Promise 都有结果(无论成功或失败)。

      1
      2
      3
      4
      5
      6
      const promises = [
      Promise.resolve(1),
      Promise.reject("失败"),
      Promise.resolve(3)
      ];
      Promise.allSettled(promises).then(results => console.log(results));
    • **Promise.race(iterable)**:返回第一个完成的 Promise(无论成功或失败)。

      1
      2
      3
      4
      5
      const promises = [
      new Promise(resolve => setTimeout(() => resolve("快"), 100)),
      new Promise(resolve => setTimeout(() => resolve("慢"), 200))
      ];
      Promise.race(promises).then(result => console.log(result)); // 输出: 快
    • **Promise.any(iterable)**:返回第一个成功的 Promise,如果全失败则返回 AggregateError。

      1
      2
      3
      4
      5
      6
      const promises = [
      Promise.reject("失败1"),
      Promise.reject("失败2"),
      Promise.resolve("成功")
      ];
      Promise.any(promises).then(result => console.log(result)); // 输出: 成功

    4. Promise 的链式调用

    每个 .then().catch() 都会返回一个新的 Promise。可以通过链式调用来组织多个异步操作。

    4.1 链式结构的简单例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    new Promise((resolve, reject) => {
    setTimeout(() => resolve("第一步完成"), 1000);
    })
    .then(result => {
    console.log(result);
    return "第二步完成";
    })
    .then(result => {
    console.log(result);
    throw new Error("第三步出错");
    })
    .catch(error => {
    console.error("捕获错误:", error);
    });

    5. 错误处理

    • 使用 .catch() 捕获错误

      1
      2
      3
      4
      5
      6
      new Promise((resolve, reject) => {
      reject("失败的原因");
      })
      .catch(error => {
      console.error("捕获错误:", error);
      });
    • .then() 的第二个参数中处理错误

      1
      2
      3
      4
      5
      6
      new Promise((resolve, reject) => {
      reject("失败的原因");
      }).then(
      result => console.log("成功:", result),
      error => console.error("捕获错误:", error)
      );
    • 使用 .finally() 方法

      1
      2
      3
      4
      5
      new Promise((resolve, reject) => {
      resolve("成功");
      })
      .finally(() => console.log("操作结束"))
      .then(result => console.log(result));

    6. Promise 的内部运行原理

    • 构造函数立即执行:Promise 的回调函数在实例化时会立即执行,异步逻辑需要手动指定。

      1
      2
      3
      4
      const p = new Promise((resolve, reject) => {
      console.log("Promise 执行了");
      resolve("完成");
      });
    • 微任务队列(Microtask Queue)thencatchfinally 的回调函数会在当前事件循环结束后立即执行(微任务优先于宏任务)。

      1
      2
      3
      4
      console.log("脚本开始");
      Promise.resolve().then(() => console.log("Promise 的 then"));
      console.log("脚本结束");
      // 输出顺序:脚本开始 -> 脚本结束 -> Promise 的 then

    7. 常见使用场景

    • AJAX 请求

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      function fetchData(url) {
      return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.open("GET", url);
      xhr.onload = () => (xhr.status === 200 ? resolve(xhr.response) : reject(xhr.statusText));
      xhr.onerror = () => reject("网络错误");
      xhr.send();
      });
      }
      fetchData("https://api.example.com/data")
      .then(data => console.log("数据:", data))
      .catch(error => console.error("错误:", error));
    • 延迟执行

      1
      2
      3
      4
      function delay(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
      }
      delay(1000).then(() => console.log("延迟1秒后执行"));
    • 任务队列

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      function task1() {
      return new Promise(resolve => setTimeout(() => resolve("任务1完成"), 1000));
      }
      function task2() {
      return new Promise(resolve => setTimeout(() => resolve("任务2完成"), 2000));
      }
      task1()
      .then(result => {
      console.log(result);
      return task2();
      })
      .then(result => console.log(result));

    8. 常见问题与注意事项

    • 回调函数未处理错误:必须始终在链的末尾添加 .catch() 来处理错误。
    • Promise 嵌套:尽量避免 Promise 嵌套,保持链式结构的清晰性。
    • Promise 状态不可逆:一旦状态变为 fulfilledrejected,就不可再更改。

    通过理解和应用这些概念和方法,你可以更好地利用 Promise 来编写高效且易于维护的异步代码。此外,结合 async/await 语法糖,可以使基于 Promise 的异步编程变得更加直观和简洁。