Promise 基础与常见陷阱

什么是Promise?

Promise是ES6(ECMAScript 2015)引入的一种异步编程解决方案,用于处理异步操作。它代表一个尚未完成但预期将来会完成的操作及其结果值。

Promise有三种状态:

  • pending:初始状态,既不是成功,也不是失败
  • fulfilled:操作成功完成
  • rejected:操作失败

基本用法

javascript 复制代码
const promise = new Promise((resolve, reject) => {
  // 异步操作
  if (/* 操作成功 */) {
    resolve(value); // 将Promise状态改为fulfilled
  } else {
    reject(error); // 将Promise状态改为rejected
  }
});

promise.then(
  value => { /* 处理成功结果 */ },
  error => { /* 处理错误 */ }
);

Promise链式调用

Promise的强大之处在于可以链式调用:

javascript 复制代码
doSomething()
  .then(result => doSomethingElse(result))
  .then(newResult => doThirdThing(newResult))
  .then(finalResult => console.log(`最终结果: ${finalResult}`))
  .catch(failureCallback); // 捕获前面所有错误

常见API

  1. Promise.resolve(value):返回一个以给定值解析后的Promise对象
  2. Promise.reject(reason):返回一个带有拒绝原因的Promise对象
  3. Promise.all(iterable):所有Promise都成功时返回结果数组,任何一个失败则立即返回失败
  4. Promise.race(iterable):返回第一个完成(成功或失败)的Promise的结果
  5. Promise.allSettled(iterable):等待所有Promise完成(无论成功或失败),返回结果数组

常见陷阱

1. 忘记返回Promise

javascript 复制代码
// 错误示例
somePromise()
  .then(val => {
    anotherPromise(val); // 忘记return
  })
  .then(result => {
    // result会是undefined
  });

2. 错误处理不当

javascript 复制代码
// 错误示例
somePromise()
  .then(val => {
    throw new Error('Oops');
  }, err => {
    // 这个错误处理函数不会捕获上面throw的错误
    console.error(err);
  });
  
// 正确做法
somePromise()
  .then(val => {
    throw new Error('Oops');
  })
  .catch(err => {
    console.error(err); // 这里会捕获错误
  });

3. Promise嵌套过深

javascript 复制代码
// 反模式 - 回调地狱的Promise版本
firstPromise()
  .then(firstResult => {
    return secondPromise(firstResult)
      .then(secondResult => {
        return thirdPromise(secondResult)
          .then(thirdResult => {
            // 深层嵌套
          });
      });
  });
  
// 改进方案
firstPromise()
  .then(firstResult => secondPromise(firstResult))
  .then(secondResult => thirdPromise(secondResult))
  .then(thirdResult => {
    // 扁平结构
  });

4. 忽略未处理的rejection

未处理的Promise rejection可能导致难以追踪的问题。建议:

javascript 复制代码
// Node.js中
process.on('unhandledRejection', (reason, promise) => {
  console.log('未处理的rejection:', reason);
});

// 浏览器中
window.addEventListener('unhandledrejection', event => {
  console.warn('未处理的rejection:', event.reason);
});

5. 混淆Promise和回调

javascript 复制代码
// 错误示例
const promise = new Promise((resolve, reject) => {
  someCallbackFunction((err, result) => {
    if (err) reject(err);
    else resolve(result);
  });
  // 忘记调用resolve/reject
});

// 正确做法
const promise = new Promise((resolve, reject) => {
  someCallbackFunction((err, result) => {
    if (err) reject(err);
    else resolve(result);
  });
});

最佳实践

  1. 总是返回Promise链中的结果,除非你确实想终止链
  2. 使用.catch()处理错误,而不是.then()的第二个参数
  3. 保持Promise链扁平,避免嵌套
  4. 考虑使用async/await语法(ES2017)来简化Promise代码
  5. 总是处理可能的rejection

Promise是现代JavaScript异步编程的基石,理解其工作原理和常见陷阱对于编写健壮的异步代码至关重要。