原生JavaScript - Promise 汇总

22 阅读6分钟

image.png

§ JS的Promise

1. Promise简介:

Promise 是 JavaScript 中处理异步操作的一种解决方案,相比传统的回调函数,它提供了更优雅、更强大的异步编程方式。

2. Promise基本概念:

2.1. Promise 状态:

Promise 有三种不可逆的状态:

  • pending(进行中):初始状态
  • fulfilled(已成功):操作成功完成
  • rejected(已失败):操作失败

2.2. 创建 Promise

const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve('操作成功');
    } else {
      reject(new Error('操作失败'));
    }
  }, 1000);
});

3. Promise核心方法

3.1. .then()- 处理成功结果

promise.then(
  (result) => {
    console.log('成功:', result);
  },
  (error) => {
    console.log('失败:', error);
  }
);

3.2. .catch()- 处理失败结果

promise
  .then(result => console.log('成功:', result))
  .catch(error => console.log('失败:', error));

3.3. .finally()- 无论成功失败都会执行

promise
  .then(result => console.log('成功:', result))
  .catch(error => console.log('失败:', error))
  .finally(() => console.log('操作完成'));

4. Promise高级用法

4.1. Promise.all()- 并行执行多个 Promise

所有 Promise 都成功时才成功,任何一个失败就立即失败

const p1 = Promise.resolve('任务1');
const p2 = Promise.resolve('任务2');
const p3 = Promise.resolve('任务3');

Promise.all([p1, p2, p3])
  .then(results => {
    console.log('所有任务完成:', results);
    // results: ['任务1', '任务2', '任务3']
  })
  .catch(error => {
    console.log('有任务失败:', error);
  });

4.2. Promise.allSettled()- 等待所有 Promise 完成

无论成功失败,都等待所有 Promise 完成

Promise.allSettled([p1, p2, p3])
  .then(results => {
    results.forEach(result => {
      if (result.status === 'fulfilled') {
        console.log('成功:', result.value);
      } else {
        console.log('失败:', result.reason);
      }
    });
  });

4.3. Promise.race()- 竞速执行

哪个 Promise 先完成就返回哪个的结果

const p1 = new Promise(resolve => setTimeout(() => resolve('p1'), 2000));
const p2 = new Promise(resolve => setTimeout(() => resolve('p2'), 1000));

Promise.race([p1, p2])
  .then(result => {
    console.log('最先完成:', result); // 输出: 'p2'
  });

4.4. Promise.any()- 任意一个成功就成功

任意一个 Promise 成功就返回成功,全部失败才返回失败

Promise.any([p1, p2, p3])
  .then(result => {
    console.log('有一个成功:', result);
  })
  .catch(errors => {
    console.log('全部失败:', errors);
  });

5. Promise使用场景

5.1. 处理异步回调地狱

// 回调地狱示例
getData(function(a) {
  getMoreData(a, function(b) {
    getMoreData(b, function(c) {
      console.log(c);
    });
  });
});

// Promise 解决方案
getData()
  .then(a => getMoreData(a))
  .then(b => getMoreData(b))
  .then(c => console.log(c))
  .catch(error => console.error(error));

5.2. 多个异步操作同步处理

// 依次执行
getUser()
  .then(user => getOrders(user.id))
  .then(orders => getOrderDetails(orders[0].id))
  .then(details => console.log(details));

// 并行执行
Promise.all([
  getUser(),
  getOrders(),
  getProducts()
])
.then(([user, orders, products]) => {
  // 处理所有数据
});

5.3. 异步依赖处理

function getData() {
  return new Promise(resolve => {
    setTimeout(() => resolve('基础数据'), 1000);
  });
}

function processData(data) {
  return new Promise(resolve => {
    setTimeout(() => resolve(`处理后的: ${data}`), 500);
  });
}

getData()
  .then(data => processData(data))
  .then(processedData => console.log(processedData));

5.4. 统一错误处理

function apiRequest(url) {
  return fetch(url)
    .then(response => {
      if (!response.ok) {
        throw new Error(`HTTP错误: ${response.status}`);
      }
      return response.json();
    });
}

// 统一错误处理
apiRequest('/api/data')
  .then(data => console.log(data))
  .catch(error => {
    console.error('请求失败:', error);
    // 可以在这里统一处理错误,如显示提示、记录日志等
  });

6. Promise静态方法

6.1. Promise.resolve()- 创建已解决的 Promise

Promise.resolve('立即值')
  .then(value => console.log(value)); // 输出: '立即值'

6.2. Promise.reject()- 创建已拒绝的 Promise

Promise.reject(new Error('错误信息'))
  .catch(error => console.log(error.message)); // 输出: '错误信息'

7. Promise链式调用

function fetchData() {
  return fetch('/api/data')
    .then(response => response.json())
    .then(data => {
      // 数据处理
      return processData(data);
    })
    .then(processedData => {
      // 进一步处理
      return saveData(processedData);
    })
    .then(savedResult => {
      console.log('保存成功:', savedResult);
      return savedResult;
    })
    .catch(error => {
      console.error('处理失败:', error);
      throw error; // 继续传递错误
    });
}

8. 手写封装Promise完整版 - 遵循Promise A+规范

8.1 手写封装:

// 定义 Promise 的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  constructor(executor) {
    // 考点1: 状态管理 - Promise 状态不可逆
    this.state = PENDING;
    this.value = undefined;  // 成功时的值
    this.reason = undefined; // 失败时的原因
    
    // 考点2: 回调队列 - 处理异步 then
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    
    // 考点3: 执行器中的 resolve
    const resolve = (value) => {
      // 考点4: 状态一旦改变就不能再变
      if (this.state === PENDING) {
        this.state = FULFILLED;
        this.value = value;
        
        // 执行所有成功的回调
        this.onFulfilledCallbacks.forEach(fn => fn());
      }
    };
    
    // 考点5: 执行器中的 reject
    const reject = (reason) => {
      if (this.state === PENDING) {
        this.state = REJECTED;
        this.reason = reason;
        
        // 执行所有失败的回调
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };
    
    // 考点6: 立即执行执行器,并进行错误捕获
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  
  // 考点7: then 方法实现(核心)
  then(onFulfilled, onRejected) {
    // 考点8: 值穿透 - 如果参数不是函数,则忽略
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
    
    // 考点9: then 必须返回一个新的 Promise
    const promise2 = new MyPromise((resolve, reject) => {
      // 考点10: 微任务模拟 - 使用 queueMicrotask
      const microTask = (fn, value) => {
        queueMicrotask(() => {
          try {
            const x = fn(value);
            // 考点11: 处理返回值 - 解析 Promise
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      };
      
      if (this.state === FULFILLED) {
        microTask(onFulfilled, this.value);
      } else if (this.state === REJECTED) {
        microTask(onRejected, this.reason);
      } else if (this.state === PENDING) {
        // 考点12: 异步情况 - 将回调存入队列
        this.onFulfilledCallbacks.push(() => {
          microTask(onFulfilled, this.value);
        });
        
        this.onRejectedCallbacks.push(() => {
          microTask(onRejected, this.reason);
        });
      }
    });
    
    return promise2;
  }
  
  // 考点13: resolvePromise 方法(Promise A+ 规范核心)
  resolvePromise(promise2, x, resolve, reject) {
    // 考点14: 防止循环引用
    if (promise2 === x) {
      return reject(new TypeError('Chaining cycle detected for promise'));
    }
    
    // 考点15: 如果 x 是 Promise
    if (x instanceof MyPromise) {
      // 考点16: 根据 x 的状态调用 resolve 或 reject
      x.then(
        y => this.resolvePromise(promise2, y, resolve, reject),
        reject
      );
    } 
    // 考点17: 如果 x 是对象或函数
    else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
      let then;
      let called = false; // 防止重复调用
      
      try {
        then = x.then;
      } catch (error) {
        return reject(error);
      }
      
      // 考点18: 如果是 thenable 对象
      if (typeof then === 'function') {
        try {
          then.call(
            x,
            y => {
              if (called) return;
              called = true;
              this.resolvePromise(promise2, y, resolve, reject);
            },
            r => {
              if (called) return;
              called = true;
              reject(r);
            }
          );
        } catch (error) {
          if (called) return;
          reject(error);
        }
      } else {
        resolve(x);
      }
    } 
    // 考点19: 普通值直接 resolve
    else {
      resolve(x);
    }
  }
  
  // 考点20: catch 方法实现
  catch(onRejected) {
    return this.then(null, onRejected);
  }
  
  // 考点21: finally 方法实现
  finally(callback) {
    return this.then(
      value => MyPromise.resolve(callback()).then(() => value),
      reason => MyPromise.resolve(callback()).then(() => { throw reason; })
    );
  }
  
  // 考点22: 静态方法 resolve
  static resolve(value) {
    // 如果已经是 Promise 实例,直接返回
    if (value instanceof MyPromise) {
      return value;
    }
    
    return new MyPromise(resolve => {
      resolve(value);
    });
  }
  
  // 考点23: 静态方法 reject
  static reject(reason) {
    return new MyPromise((_, reject) => {
      reject(reason);
    });
  }
  
  // 考点24: 静态方法 all
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      const results = [];
      let completedCount = 0;
      
      // 处理空数组
      if (promises.length === 0) {
        return resolve(results);
      }
      
      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          value => {
            results[index] = value;
            completedCount++;
            
            // 所有 Promise 都完成
            if (completedCount === promises.length) {
              resolve(results);
            }
          },
          // 任一 Promise 失败
          reject
        );
      });
    });
  }
  
  // 考点25: 静态方法 race
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      promises.forEach(promise => {
        MyPromise.resolve(promise).then(resolve, reject);
      });
    });
  }
  
  // 考点26: 静态方法 allSettled
  static allSettled(promises) {
    return new MyPromise(resolve => {
      const results = [];
      let settledCount = 0;
      
      const processResult = (index, status, valueOrReason) => {
        results[index] = {
          status,
          [status === 'fulfilled' ? 'value' : 'reason']: valueOrReason
        };
        settledCount++;
        
        if (settledCount === promises.length) {
          resolve(results);
        }
      };
      
      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          value => processResult(index, 'fulfilled', value),
          reason => processResult(index, 'rejected', reason)
        );
      });
    });
  }
  
  // 考点27: 静态方法 any
  static any(promises) {
    return new MyPromise((resolve, reject) => {
      const errors = [];
      let rejectedCount = 0;
      
      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          resolve,
          reason => {
            errors[index] = reason;
            rejectedCount++;
            
            // 所有都失败
            if (rejectedCount === promises.length) {
              reject(new AggregateError(errors, 'All promises were rejected'));
            }
          }
        );
      });
    });
  }
}

如何使用:

// 测试1: 基础功能
const p1 = new MyPromise((resolve, reject) => {
  setTimeout(() => resolve('success'), 1000);
});

p1.then(res => console.log('Test1:', res)); // 1秒后输出: Test1: success

// 测试2: 链式调用
new MyPromise(resolve => resolve(1))
  .then(res => {
    console.log('Test2-1:', res); // 输出: Test2-1: 1
    return res + 1;
  })
  .then(res => {
    console.log('Test2-2:', res); // 输出: Test2-2: 2
    return new MyPromise(resolve => resolve(res + 1));
  })
  .then(res => {
    console.log('Test2-3:', res); // 输出: Test2-3: 3
  });

// 测试3: Promise.all
MyPromise.all([
  MyPromise.resolve(1),
  MyPromise.resolve(2),
  MyPromise.resolve(3)
]).then(res => console.log('Test3:', res)); // 输出: Test3: [1, 2, 3]

// 测试4: Promise.race
MyPromise.race([
  new MyPromise(resolve => setTimeout(() => resolve('fast'), 100)),
  new MyPromise(resolve => setTimeout(() => resolve('slow'), 200))
]).then(res => console.log('Test4:', res)); // 输出: Test4: fast

// 测试5: 错误处理
new MyPromise((_, reject) => reject('error'))
  .then(() => console.log('不会执行'))
  .catch(err => console.log('Test5:', err)) // 输出: Test5: error
  .finally(() => console.log('Test5: finally executed'));

// 测试6: 值穿透
MyPromise.resolve(1)
  .then(2)  // 2 不是函数,被忽略
  .then(res => console.log('Test6:', res)); // 输出: Test6: 1

9. 面试官可能会问的问题:

Q1: Promise 的状态可以改变吗?

A: Promise 的状态一旦改变(从 pending 到 fulfilled 或 rejected)就不可再次改变。

Q2: then 方法为什么能链式调用?

A: then 方法返回一个新的 Promise,这个新 Promise 的状态由 then 中回调函数的返回值决定。

Q3: Promise 的异步如何实现?

A: 通过微任务(queueMicrotask、MutationObserver、process.nextTick 等)实现,确保在当前任务执行完后、渲染前执行。

Q4: Promise.resolve 和 new Promise(resolve => resolve(value)) 有什么区别?

A: Promise.resolve 是快捷方式,如果参数已经是 Promise,会直接返回这个 Promise。

Q5: Promise.all 和 Promise.allSettled 的区别?

A:

  • all: 所有成功才成功,一个失败就失败
  • allSettled: 无论成功失败,都等待所有完成

Q6: 如何处理 Promise 中的错误?

A: 通过 catch 方法捕获,或者 then 的第二个参数。注意:catch 能捕获前面所有 then 中的错误。