Promise 的概念与背景

Promise 是 JavaScript 中处理异步操作的一种机制,用于解决回调地狱(Callback Hell)的问题。通过链式调用和状态管理,Promise 提供了一种更优雅的方式来处理异步任务。在 ES6 中,Promise 被正式纳入标准,成为现代 JavaScript 开发的核心工具之一。

Promise 代表一个异步操作的最终完成或失败,并返回结果值。它有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。状态一旦改变,就不能再变。

Promise 的基本用法

创建一个 Promise 对象需要传入一个执行器函数(executor),该函数接收两个参数:resolve 和 reject。resolve 用于将 Promise 状态改为 fulfilled,reject 用于将状态改为 rejected。

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功的结果');
  }, 1000);
});

promise.then(result => {
  console.log(result); // 输出:成功的结果
}).catch(error => {
  console.error(error);
});

Promise 的链式调用

Promise 的 then 方法返回一个新的 Promise,这使得链式调用成为可能。每次 then 调用都可以处理前一个 Promise 的结果,并返回一个新的值或 Promise。

fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    console.log(data);
    return fetch('/api/other-data');
  })
  .then(response => response.json())
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('发生错误:', error);
  });

Promise 的错误处理

Promise 的错误可以通过 catch 方法捕获。catch 实际上是 then(null, rejection) 的语法糖。建议在链式调用的末尾使用 catch,以避免遗漏错误。

someAsyncFunction()
  .then(result => {
    console.log(result);
    return anotherAsyncFunction();
  })
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error('捕获错误:', error);
  });

Promise 的静态方法

Promise 提供了一些静态方法,用于处理多个 Promise 或创建特定状态的 Promise。

  • Promise.resolve(value):返回一个以给定值解析后的 Promise。
  • Promise.reject(reason):返回一个以给定原因拒绝的 Promise。
  • Promise.all(iterable):等待所有 Promise 完成,或第一个 Promise 拒绝。
  • Promise.race(iterable):返回第一个完成或拒绝的 Promise 的结果。
  • Promise.allSettled(iterable):等待所有 Promise 完成(无论成功或失败)。
  • Promise.any(iterable):返回第一个成功的 Promise 的结果。
const promise1 = Promise.resolve(1);
const promise2 = Promise.reject('错误');
const promise3 = new Promise(resolve => setTimeout(resolve, 100, 'foo'));

Promise.all([promise1, promise3])
  .then(values => {
    console.log(values); // [1, 'foo']
  })
  .catch(error => {
    console.error(error);
  });

Promise.race([promise1, promise3])
  .then(value => {
    console.log(value); // 1
  });

Promise 的微任务机制

Promise 的回调函数(then/catch/finally)会被放入微任务队列(Microtask Queue),而不是宏任务队列(Macrotask Queue)。微任务会在当前任务执行完成后立即执行,优先级高于宏任务(如 setTimeout)。

console.log('开始');

setTimeout(() => {
  console.log('宏任务');
}, 0);

Promise.resolve()
  .then(() => {
    console.log('微任务');
  });

console.log('结束');

// 输出顺序:
// 开始
// 结束
// 微任务
// 宏任务

Promise 的实现原理

Promise 的核心是通过状态管理和回调队列实现的。以下是一个简化的 Promise 实现:

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());
      }
    };

    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  then(onFulfilled, onRejected) {
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.state === 'fulfilled') {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      } else if (this.state === 'rejected') {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      } else {
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
      }
    });
    return promise2;
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('循环引用'));
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject);
  } else {
    resolve(x);
  }
}

Promise 的常见问题与解决

问题 1:回调未捕获的错误 Promise 内部的错误如果不被捕获,会导致静默失败。建议始终使用 catch 或 try-catch 包裹异步代码。

问题 2:Promise 的取消 原生 Promise 不支持取消,但可以通过封装或使用 AbortController 实现类似功能。

function cancellablePromise(executor) {
  let rejectFn;
  const promise = new Promise((resolve, reject) => {
    rejectFn = reject;
    executor(resolve, reject);
  });
  promise.cancel = () => {
    rejectFn(new Error('Promise 被取消'));
  };
  return promise;
}

const p = cancellablePromise((resolve, reject) => {
  setTimeout(() => resolve('完成'), 2000);
});
p.then(console.log).catch(console.error);
p.cancel(); // 输出:Error: Promise 被取消

Promise 的性能优化

避免不必要的 Promise 嵌套,尽量扁平化链式调用。对于多个独立的异步操作,使用 Promise.all 并行执行。

// 不推荐
getUser()
  .then(user => {
    getOrders(user.id)
      .then(orders => {
        console.log(orders);
      });
  });

// 推荐
getUser()
  .then(user => getOrders(user.id))
  .then(orders => {
    console.log(orders);
  });

// 并行请求
Promise.all([getUser(), getOrders()])
  .then(([user, orders]) => {
    console.log(user, orders);
  });

Promise 与 async/await 的关系

async/await 是 Promise 的语法糖,让异步代码看起来像同步代码。async 函数总是返回一个 Promise,await 用于等待 Promise 的解决。

async function fetchData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

Promise 的高级技巧

延迟执行 通过封装 Promise 实现延迟执行逻辑。

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

delay(1000).then(() => console.log('1秒后执行'));

超时控制 结合 Promise.race 实现超时机制。

function timeout(promise, ms) {
  return Promise.race([
    promise,
    delay(ms).then(() => Promise.reject('超时'))
  ]);
}

timeout(fetch('/api/data'), 5000)
  .then(response => console.log(response))
  .catch(error => console.error(error));

Promise 的适用场景

Promise 适用于任何需要处理异步操作的场景,如:

  • 网络请求(fetch/AJAX)
  • 文件读写(Node.js)
  • 定时器操作
  • 数据库查询
  • 用户交互事件

Promise 的局限性

  • 无法取消(需自行实现)
  • 错误处理必须显式捕获
  • 某些场景下可能仍需回调(如事件监听)
  • 嵌套过多时仍可能降低可读性

Promise 的最佳实践

  1. 始终返回 Promise 链中的值,避免中断链式调用。
  2. 使用 async/await 简化复杂逻辑。
  3. 为每个 Promise 添加错误处理。
  4. 避免在 Promise 中抛出同步错误。
  5. 合理使用 Promise 静态方法处理多个异步任务。

Promise 的 polyfill 与兼容性

对于不支持 Promise 的环境(如旧版浏览器),可以通过 polyfill 实现兼容。常用的 polyfill 库包括:

  • es6-promise
  • promise-polyfill
  • core-js
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4.2.8/dist/es6-promise.auto.min.js"></script>

Promise 的调试技巧

在开发中,可以通过以下方式调试 Promise:

  • 使用 console.log 打印 Promise 状态
  • 在 catch 中输出完整错误堆栈
  • 使用调试工具的异步断点功能
  • 添加 unhandledrejection 事件监听全局未捕获错误
window.addEventListener('unhandledrejection', event => {
  console.error('未捕获的 Promise 错误:', event.reason);
});

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐