未找到匹配的笔记

1.30 - Promise原理、手写Promise

JavaScriptPromise面试

Promise 深度解析与手写实现

从原理到实践,彻底掌握 Promise 的核心机制

目录

  1. Promise 基础概念
  2. Promise 核心原理
  3. 手写 Promise - 基础版
  4. 手写 Promise - 完整版
  5. Promise 静态方法实现
  6. Promise 高级特性
  7. 常见面试题
  8. 实战应用场景

一、Promise 基础概念

1.1 什么是 Promise?

Promise 是异步编程的一种解决方案,比传统的回调函数和事件更合理、更强大。它是一个对象,用来表示一个异步操作的最终完成(或失败)及其结果值。

1.2 Promise 的三种状态

// Promise 有且只有三种状态
const PENDING = 'pending';     // 初始状态,既不是成功,也不是失败
const FULFILLED = 'fulfilled'; // 操作成功完成
const REJECTED = 'rejected';   // 操作失败

状态转换规则(核心):

  • 状态只能从 pendingfulfilledpendingrejected
  • 状态一旦改变,就不会再变,任何时候都可以得到这个结果
  • fulfilledrejected 状态不可逆,不能相互转换

1.3 Promise 的基本使用

// 创建 Promise
const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    const success = Math.random() > 0.5;
    if (success) {
      resolve('操作成功'); // 将状态改为 fulfilled
    } else {
      reject('操作失败');  // 将状态改为 rejected
    }
  }, 1000);
});

// 使用 Promise
promise
  .then(
    result => console.log(result),  // 成功回调
    error => console.log(error)     // 失败回调
  )
  .catch(error => console.log(error)) // 捕获错误
  .finally(() => console.log('完成')); // 无论成功失败都执行

二、Promise 核心原理

2.1 Promise 的核心机制

Promise 的实现基于以下几个核心机制:

1. 状态管理

class MyPromise {
  constructor(executor) {
    this.state = 'pending';      // 当前状态
    this.value = undefined;      // 成功的值
    this.reason = undefined;     // 失败的原因
  }
}

2. 回调队列

class MyPromise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];  // 成功回调队列
    this.onRejectedCallbacks = [];   // 失败回调队列
  }
}

为什么需要回调队列?

  • Promise 可能在异步操作完成前就调用 .then()
  • 此时状态还是 pending,需要先存储回调函数
  • 等状态改变后,再依次执行队列中的回调

3. then 方法链式调用

// then 方法必须返回一个新的 Promise
promise
  .then(result => result * 2)
  .then(result => result + 1)
  .then(result => console.log(result));

2.2 Promise 执行流程图

创建 Promise

执行 executor

同步/异步操作

  ┌─────────┬─────────┐
  │         │         │
resolve   reject    pending
  │         │         │
fulfilled rejected  等待中
  │         │         │
执行成功  执行失败  存储回调
回调队列  回调队列    到队列
  │         │         │
  └─────────┴─────────┘

    返回新 Promise

      链式调用

2.3 关键概念理解

Promise Resolution Procedure(Promise 解决过程)

这是 Promise 最复杂的部分,用于处理 then 返回值:

// 情况 1: 返回普通值
promise.then(() => 123); // 新 Promise 的值是 123

// 情况 2: 返回新的 Promise
promise.then(() => new Promise(resolve => resolve(456))); // 需要等待这个 Promise

// 情况 3: 返回自身
const p = promise.then(() => p); // 需要抛出错误,避免循环引用

// 情况 4: 返回 thenable 对象
promise.then(() => ({
  then: (resolve) => resolve(789)
})); // 需要调用其 then 方法

三、手写 Promise - 基础版

3.1 最简单的 Promise 实现

class MyPromise {
  constructor(executor) {
    // 初始化状态
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;

    // resolve 函数
    const resolve = (value) => {
      // 只有 pending 状态才能转换
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
      }
    };

    // reject 函数
    const reject = (reason) => {
      // 只有 pending 状态才能转换
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
      }
    };

    // 立即执行 executor
    try {
      executor(resolve, reject);
    } catch (error) {
      // executor 执行出错,直接 reject
      reject(error);
    }
  }

  // then 方法
  then(onFulfilled, onRejected) {
    // 根据状态执行对应回调
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    }
    if (this.state === 'rejected') {
      onRejected(this.reason);
    }
  }
}

// 测试
const promise = new MyPromise((resolve, reject) => {
  resolve('成功');
});

promise.then(
  value => console.log(value),   // 输出: 成功
  reason => console.log(reason)
);

这个版本的问题:

  1. ❌ 不支持异步操作
  2. ❌ 不支持链式调用
  3. ❌ 没有处理回调队列

3.2 支持异步操作

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) {
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    }
    if (this.state === 'rejected') {
      onRejected(this.reason);
    }
    // 新增:处理异步情况
    if (this.state === 'pending') {
      // 将回调存入队列
      this.onFulfilledCallbacks.push(() => {
        onFulfilled(this.value);
      });
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason);
      });
    }
  }
}

// 测试异步
const promise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('异步成功');
  }, 1000);
});

promise.then(
  value => console.log(value),   // 1秒后输出: 异步成功
  reason => console.log(reason)
);

四、手写 Promise - 完整版

4.1 符合 Promises/A+ 规范的完整实现

class MyPromise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      // 如果 resolve 的是一个 Promise,需要等待其完成
      if (value instanceof MyPromise) {
        return value.then(resolve, reject);
      }

      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) {
    // 参数校验,确保是函数
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };

    // 返回新的 Promise,实现链式调用
    const promise2 = new MyPromise((resolve, reject) => {
      
      // 封装成功回调
      const fulfilledMicrotask = () => {
        // 使用 queueMicrotask 模拟微任务
        queueMicrotask(() => {
          try {
            const x = onFulfilled(this.value);
            // Promise 解决过程
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      };

      // 封装失败回调
      const rejectedMicrotask = () => {
        queueMicrotask(() => {
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      };

      // 根据状态执行对应逻辑
      if (this.state === 'fulfilled') {
        fulfilledMicrotask();
      } else if (this.state === 'rejected') {
        rejectedMicrotask();
      } else if (this.state === 'pending') {
        this.onFulfilledCallbacks.push(fulfilledMicrotask);
        this.onRejectedCallbacks.push(rejectedMicrotask);
      }
    });

    return promise2;
  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }

  finally(onFinally) {
    return this.then(
      value => MyPromise.resolve(onFinally()).then(() => value),
      reason => MyPromise.resolve(onFinally()).then(() => { throw reason })
    );
  }
}

/**
 * Promise 解决过程
 * 这是 Promise 最核心的部分
 */
function resolvePromise(promise2, x, resolve, reject) {
  // 1. 如果 promise2 和 x 指向同一对象,抛出 TypeError
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise'));
  }

  // 2. 如果 x 是 Promise 实例
  if (x instanceof MyPromise) {
    x.then(resolve, reject);
    return;
  }

  // 3. 如果 x 是对象或函数
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    let called = false; // 防止多次调用

    try {
      const then = x.then;

      // 如果 then 是函数,认为 x 是 thenable 对象
      if (typeof then === 'function') {
        then.call(
          x,
          y => {
            if (called) return;
            called = true;
            // 递归解析
            resolvePromise(promise2, y, resolve, reject);
          },
          r => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        // then 不是函数,直接 resolve
        resolve(x);
      }
    } catch (error) {
      if (called) return;
      called = true;
      reject(error);
    }
  } else {
    // 4. x 是普通值,直接 resolve
    resolve(x);
  }
}

4.2 关键点详解

1. 为什么需要 queueMicrotask?

// 原生 Promise 的回调是微任务
Promise.resolve().then(() => console.log(1));
console.log(2);
// 输出: 2, 1

// 我们的实现也要保证这个顺序
new MyPromise(resolve => resolve()).then(() => console.log(1));
console.log(2);
// 输出: 2, 1

2. resolvePromise 为什么这么复杂?

// 场景 1: 返回普通值
promise.then(() => 123);

// 场景 2: 返回 Promise
promise.then(() => new Promise(resolve => resolve(456)));

// 场景 3: 返回 thenable 对象
promise.then(() => ({
  then: (resolve) => setTimeout(() => resolve(789), 1000)
}));

// 场景 4: 循环引用
const p = promise.then(() => p); // 必须抛出错误

// 场景 5: 嵌套 Promise
promise.then(() => 
  new Promise(resolve => 
    resolve(new Promise(r => r(999)))
  )
);

3. called 标志的作用

// 防止 thenable 对象多次调用 resolve/reject
const thenable = {
  then: (resolve, reject) => {
    resolve(1);
    resolve(2); // 应该被忽略
    reject(3);  // 应该被忽略
  }
};

MyPromise.resolve(thenable).then(value => {
  console.log(value); // 只输出 1
});

五、Promise 静态方法实现

5.1 Promise.resolve

MyPromise.resolve = function(value) {
  // 如果是 Promise 实例,直接返回
  if (value instanceof MyPromise) {
    return value;
  }

  // 如果是 thenable 对象
  if (value !== null && typeof value === 'object' && typeof value.then === 'function') {
    return new MyPromise((resolve, reject) => {
      value.then(resolve, reject);
    });
  }

  // 普通值,返回成功的 Promise
  return new MyPromise(resolve => resolve(value));
};

// 测试
MyPromise.resolve(123).then(value => console.log(value)); // 123
MyPromise.resolve(MyPromise.resolve(456)).then(value => console.log(value)); // 456

5.2 Promise.reject

MyPromise.reject = function(reason) {
  // 无论传入什么,都返回失败的 Promise
  return new MyPromise((resolve, reject) => reject(reason));
};

// 测试
MyPromise.reject('error').catch(reason => console.log(reason)); // error

5.3 Promise.all

MyPromise.all = function(promises) {
  return new MyPromise((resolve, reject) => {
    // 参数校验
    if (!Array.isArray(promises)) {
      return reject(new TypeError('Argument must be an array'));
    }

    const results = [];
    let completedCount = 0;
    const length = promises.length;

    // 空数组直接返回
    if (length === 0) {
      return resolve(results);
    }

    promises.forEach((promise, index) => {
      // 将非 Promise 值转为 Promise
      MyPromise.resolve(promise).then(
        value => {
          results[index] = value;
          completedCount++;

          // 所有 Promise 都完成
          if (completedCount === length) {
            resolve(results);
          }
        },
        reason => {
          // 任何一个失败,立即 reject
          reject(reason);
        }
      );
    });
  });
};

// 测试
MyPromise.all([
  MyPromise.resolve(1),
  MyPromise.resolve(2),
  MyPromise.resolve(3)
]).then(values => console.log(values)); // [1, 2, 3]

MyPromise.all([
  MyPromise.resolve(1),
  MyPromise.reject('error'),
  MyPromise.resolve(3)
]).catch(reason => console.log(reason)); // error

5.4 Promise.race

MyPromise.race = function(promises) {
  return new MyPromise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      return reject(new TypeError('Argument must be an array'));
    }

    // 空数组,永远 pending
    if (promises.length === 0) {
      return;
    }

    promises.forEach(promise => {
      // 第一个完成的 Promise 决定结果
      MyPromise.resolve(promise).then(resolve, reject);
    });
  });
};

// 测试
MyPromise.race([
  new MyPromise(resolve => setTimeout(() => resolve(1), 1000)),
  new MyPromise(resolve => setTimeout(() => resolve(2), 500)),
  new MyPromise(resolve => setTimeout(() => resolve(3), 800))
]).then(value => console.log(value)); // 500ms 后输出 2

5.5 Promise.allSettled

MyPromise.allSettled = function(promises) {
  return new MyPromise((resolve) => {
    if (!Array.isArray(promises)) {
      return reject(new TypeError('Argument must be an array'));
    }

    const results = [];
    let completedCount = 0;
    const length = promises.length;

    if (length === 0) {
      return resolve(results);
    }

    promises.forEach((promise, index) => {
      MyPromise.resolve(promise).then(
        value => {
          results[index] = { status: 'fulfilled', value };
          completedCount++;
          if (completedCount === length) {
            resolve(results);
          }
        },
        reason => {
          results[index] = { status: 'rejected', reason };
          completedCount++;
          if (completedCount === length) {
            resolve(results);
          }
        }
      );
    });
  });
};

// 测试
MyPromise.allSettled([
  MyPromise.resolve(1),
  MyPromise.reject('error'),
  MyPromise.resolve(3)
]).then(results => console.log(results));
// [
//   { status: 'fulfilled', value: 1 },
//   { status: 'rejected', reason: 'error' },
//   { status: 'fulfilled', value: 3 }
// ]

5.6 Promise.any

MyPromise.any = function(promises) {
  return new MyPromise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      return reject(new TypeError('Argument must be an array'));
    }

    const errors = [];
    let rejectedCount = 0;
    const length = promises.length;

    if (length === 0) {
      return reject(new AggregateError([], 'All promises were rejected'));
    }

    promises.forEach((promise, index) => {
      MyPromise.resolve(promise).then(
        value => {
          // 任何一个成功,立即 resolve
          resolve(value);
        },
        reason => {
          errors[index] = reason;
          rejectedCount++;

          // 所有都失败,才 reject
          if (rejectedCount === length) {
            reject(new AggregateError(errors, 'All promises were rejected'));
          }
        }
      );
    });
  });
};

// 测试
MyPromise.any([
  MyPromise.reject('error1'),
  MyPromise.resolve(2),
  MyPromise.reject('error3')
]).then(value => console.log(value)); // 2

MyPromise.any([
  MyPromise.reject('error1'),
  MyPromise.reject('error2')
]).catch(error => console.log(error)); // AggregateError

六、Promise 高级特性

6.1 Promise 链式调用的值传递

// 值穿透
Promise.resolve(1)
  .then()           // 没有处理函数
  .then()           // 没有处理函数
  .then(value => {
    console.log(value); // 1
  });

// 实现原理
then(onFulfilled, onRejected) {
  // 如果不是函数,提供默认函数
  onFulfilled = typeof onFulfilled === 'function' 
    ? onFulfilled 
    : value => value; // 值穿透

  onRejected = typeof onRejected === 'function' 
    ? onRejected 
    : reason => { throw reason }; // 错误穿透
}

6.2 错误处理机制

// 错误会一直传递,直到被 catch
Promise.resolve()
  .then(() => {
    throw new Error('错误1');
  })
  .then(() => {
    console.log('不会执行');
  })
  .then(() => {
    console.log('不会执行');
  })
  .catch(error => {
    console.log(error.message); // 错误1
    return 'recovered';
  })
  .then(value => {
    console.log(value); // recovered
  });

6.3 Promise 的中断

// Promise 没有官方的中断方法,但可以通过返回一个永远 pending 的 Promise 来"中断"
function interruptPromise() {
  return new Promise(() => {}); // 永远不 resolve 也不 reject
}

Promise.resolve()
  .then(() => {
    console.log('step 1');
    return interruptPromise(); // 后续不会执行
  })
  .then(() => {
    console.log('不会执行');
  });

6.4 Promise 的重试机制

/**
 * Promise 重试函数
 * @param {Function} fn - 需要重试的函数
 * @param {Number} times - 重试次数
 * @param {Number} delay - 重试延迟
 */
function retry(fn, times = 3, delay = 1000) {
  return new Promise((resolve, reject) => {
    function attempt(remainingTimes) {
      fn()
        .then(resolve)
        .catch(error => {
          if (remainingTimes === 0) {
            reject(error);
          } else {
            console.log(`失败,${delay}ms 后重试,剩余次数: ${remainingTimes}`);
            setTimeout(() => {
              attempt(remainingTimes - 1);
            }, delay);
          }
        });
    }
    attempt(times);
  });
}

// 使用示例
let count = 0;
const unstableApi = () => {
  return new Promise((resolve, reject) => {
    count++;
    if (count < 3) {
      reject(`失败 ${count} 次`);
    } else {
      resolve('成功');
    }
  });
};

retry(unstableApi, 5, 500)
  .then(result => console.log(result))
  .catch(error => console.log(error));

6.5 Promise 并发控制

/**
 * 并发控制函数
 * @param {Array} tasks - 任务数组
 * @param {Number} limit - 并发限制
 */
function concurrentControl(tasks, limit = 2) {
  return new Promise((resolve) => {
    const results = [];
    let runningCount = 0;
    let completedCount = 0;
    let index = 0;

    function run() {
      while (runningCount < limit && index < tasks.length) {
        const currentIndex = index;
        const task = tasks[index++];
        runningCount++;

        task()
          .then(result => {
            results[currentIndex] = result;
          })
          .catch(error => {
            results[currentIndex] = error;
          })
          .finally(() => {
            runningCount--;
            completedCount++;

            if (completedCount === tasks.length) {
              resolve(results);
            } else {
              run();
            }
          });
      }
    }

    run();
  });
}

// 使用示例
const tasks = [
  () => new Promise(resolve => setTimeout(() => resolve('任务1'), 1000)),
  () => new Promise(resolve => setTimeout(() => resolve('任务2'), 500)),
  () => new Promise(resolve => setTimeout(() => resolve('任务3'), 800)),
  () => new Promise(resolve => setTimeout(() => resolve('任务4'), 300)),
  () => new Promise(resolve => setTimeout(() => resolve('任务5'), 600))
];

concurrentControl(tasks, 2).then(results => {
  console.log(results);
});

七、常见面试题

7.1 Promise 执行顺序题

console.log('1');

setTimeout(() => {
  console.log('2');
  Promise.resolve().then(() => {
    console.log('3');
  });
}, 0);

new Promise((resolve) => {
  console.log('4');
  resolve();
}).then(() => {
  console.log('5');
}).then(() => {
  console.log('6');
});

console.log('7');

// 输出顺序: 1, 4, 7, 5, 6, 2, 3

/**
 * 解析:
 * 1. 同步代码: 1, 4, 7
 * 2. 微任务队列: 5 -> 6
 * 3. 宏任务队列: 2 -> 微任务 3
 */

7.2 Promise 红绿灯问题

// 红灯3秒亮一次,绿灯2秒亮一次,黄灯1秒亮一次
// 实现一个函数让三个灯循环亮起

function red() {
  console.log('红灯');
}
function green() {
  console.log('绿灯');
}
function yellow() {
  console.log('黄灯');
}

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

function step() {
  Promise.resolve()
    .then(() => light(red, 3000))
    .then(() => light(green, 2000))
    .then(() => light(yellow, 1000))
    .then(() => step()); // 循环
}

step();

7.3 实现 Promise.map

/**
 * Promise.map
 * 类似 Promise.all,但可以控制并发数
 */
Promise.map = function(promises, concurrency = Infinity) {
  return new Promise((resolve, reject) => {
    const results = [];
    const total = promises.length;
    let completed = 0;
    let running = 0;
    let index = 0;

    function run() {
      if (completed === total) {
        return resolve(results);
      }

      while (running < concurrency && index < total) {
        const currentIndex = index;
        running++;
        index++;

        Promise.resolve(promises[currentIndex])
          .then(value => {
            results[currentIndex] = value;
            completed++;
            running--;
            run();
          })
          .catch(reject);
      }
    }

    run();
  });
};

// 测试
const tasks = [
  () => new Promise(resolve => setTimeout(() => resolve(1), 1000)),
  () => new Promise(resolve => setTimeout(() => resolve(2), 500)),
  () => new Promise(resolve => setTimeout(() => resolve(3), 800))
];

Promise.map(tasks.map(task => task()), 2).then(console.log);

7.4 实现 promisify

/**
 * 将 callback 风格的函数转为 Promise 风格
 */
function promisify(fn) {
  return function(...args) {
    return new Promise((resolve, reject) => {
      fn(...args, (error, result) => {
        if (error) {
          reject(error);
        } else {
          resolve(result);
        }
      });
    });
  };
}

// 使用示例
const fs = require('fs');
const readFilePromise = promisify(fs.readFile);

readFilePromise('file.txt', 'utf8')
  .then(content => console.log(content))
  .catch(error => console.error(error));

7.5 实现 Promise 串行执行

/**
 * 串行执行 Promise
 */
function sequencePromises(promises) {
  return promises.reduce((prev, current) => {
    return prev.then(current);
  }, Promise.resolve());
}

// 使用示例
const tasks = [
  () => new Promise(resolve => {
    setTimeout(() => {
      console.log('任务1');
      resolve();
    }, 1000);
  }),
  () => new Promise(resolve => {
    setTimeout(() => {
      console.log('任务2');
      resolve();
    }, 500);
  }),
  () => new Promise(resolve => {
    setTimeout(() => {
      console.log('任务3');
      resolve();
    }, 800);
  })
];

sequencePromises(tasks);
// 输出: 任务1 (1秒后) -> 任务2 (0.5秒后) -> 任务3 (0.8秒后)

八、实战应用场景

8.1 请求超时控制

function timeoutPromise(promise, timeout = 5000) {
  return Promise.race([
    promise,
    new Promise((_, reject) => {
      setTimeout(() => {
        reject(new Error(`请求超时,超过 ${timeout}ms`));
      }, timeout);
    })
  ]);
}

// 使用示例
const fetchData = fetch('https://api.example.com/data');

timeoutPromise(fetchData, 3000)
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));

8.2 图片预加载

function loadImage(url) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.onerror = () => reject(new Error(`图片加载失败: ${url}`));
    img.src = url;
  });
}

function preloadImages(urls) {
  const promises = urls.map(url => loadImage(url));
  return Promise.all(promises);
}

// 使用示例
const imageUrls = [
  'image1.jpg',
  'image2.jpg',
  'image3.jpg'
];

preloadImages(imageUrls)
  .then(images => {
    console.log('所有图片加载完成', images);
  })
  .catch(error => {
    console.error('图片加载失败', error);
  });

8.3 缓存 Promise 结果

function cachePromise(fn) {
  let cache = null;
  let cacheTime = 0;
  const CACHE_DURATION = 60000; // 缓存1分钟

  return function(...args) {
    const now = Date.now();
    
    // 缓存未过期,直接返回
    if (cache && now - cacheTime < CACHE_DURATION) {
      return Promise.resolve(cache);
    }

    // 执行函数并缓存结果
    return fn(...args).then(result => {
      cache = result;
      cacheTime = now;
      return result;
    });
  };
}

// 使用示例
const fetchUserData = cachePromise((userId) => {
  return fetch(`https://api.example.com/users/${userId}`)
    .then(response => response.json());
});

// 第一次调用,发起请求
fetchUserData(123).then(data => console.log(data));

// 1分钟内再次调用,直接返回缓存
fetchUserData(123).then(data => console.log(data));

8.4 Promise 请求去重

/**
 * 相同的请求在 pending 期间,只发起一次
 */
function dedupeFetch() {
  const pendingPromises = new Map();

  return function(url, options) {
    const key = `${url}_${JSON.stringify(options)}`;

    // 如果已有相同请求在 pending,返回该 Promise
    if (pendingPromises.has(key)) {
      return pendingPromises.get(key);
    }

    // 创建新请求
    const promise = fetch(url, options)
      .then(response => response.json())
      .finally(() => {
        // 请求完成后删除
        pendingPromises.delete(key);
      });

    pendingPromises.set(key, promise);
    return promise;
  };
}

const fetchData = dedupeFetch();

// 同时发起三个相同请求,实际只会发送一次
fetchData('/api/data', { method: 'GET' }).then(console.log);
fetchData('/api/data', { method: 'GET' }).then(console.log);
fetchData('/api/data', { method: 'GET' }).then(console.log);

九、总结与面试要点

9.1 核心知识点

  1. Promise 三种状态

    • pending, fulfilled, rejected
    • 状态只能单向转换,不可逆
  2. Promise 的关键机制

    • 回调队列(处理异步)
    • 链式调用(then 返回新 Promise)
    • 值传递(resolvePromise)
  3. 微任务与宏任务

    • Promise.then 是微任务
    • setTimeout 是宏任务
    • 微任务优先于宏任务执行
  4. 错误处理

    • catch 可以捕获 Promise 链中的所有错误
    • finally 无论成功失败都会执行
    • 未捕获的 Promise 错误会导致 unhandledRejection

9.2 面试高频问题

Q1: Promise 解决了什么问题?

  • 解决回调地狱(callback hell)
  • 统一异步操作的接口
  • 更好的错误处理机制
  • 支持链式调用

Q2: Promise 和 async/await 的区别?

  • async/await 是 Promise 的语法糖
  • async/await 使异步代码看起来像同步代码
  • 错误处理: Promise 用 catch,async/await 用 try/catch

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

  • Promise.all: 任何一个失败就立即 reject
  • Promise.allSettled: 等待所有 Promise 完成,无论成功失败

Q4: 如何取消一个 Promise?

  • Promise 本身不支持取消
  • 可以使用 AbortController(Fetch API)
  • 或返回一个永远 pending 的 Promise 来”中断”链条

9.3 手写能力检查清单

✅ 能实现基础的 Promise(状态管理、resolve/reject) ✅ 能实现 then 方法的链式调用 ✅ 能实现 resolvePromise(处理各种返回值) ✅ 能实现 Promise.all / race / allSettled / any ✅ 能实现 Promise.resolve / reject ✅ 理解微任务与宏任务的执行顺序 ✅ 能解决实际问题(并发控制、重试、超时等)


十、完整可测试代码

/**
 * 完整的 MyPromise 实现
 * 符合 Promises/A+ 规范
 */
class MyPromise {
  static PENDING = 'pending';
  static FULFILLED = 'fulfilled';
  static REJECTED = 'rejected';

  constructor(executor) {
    this.state = MyPromise.PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (value instanceof MyPromise) {
        return value.then(resolve, reject);
      }
      if (this.state === MyPromise.PENDING) {
        this.state = MyPromise.FULFILLED;
        this.value = value;
        this.onFulfilledCallbacks.forEach(fn => fn());
      }
    };

    const reject = (reason) => {
      if (this.state === MyPromise.PENDING) {
        this.state = MyPromise.REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };

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

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };

    const promise2 = new MyPromise((resolve, reject) => {
      const fulfilledMicrotask = () => {
        queueMicrotask(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      };

      const rejectedMicrotask = () => {
        queueMicrotask(() => {
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      };

      if (this.state === MyPromise.FULFILLED) {
        fulfilledMicrotask();
      } else if (this.state === MyPromise.REJECTED) {
        rejectedMicrotask();
      } else {
        this.onFulfilledCallbacks.push(fulfilledMicrotask);
        this.onRejectedCallbacks.push(rejectedMicrotask);
      }
    });

    return promise2;
  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }

  finally(onFinally) {
    return this.then(
      value => MyPromise.resolve(onFinally()).then(() => value),
      reason => MyPromise.resolve(onFinally()).then(() => { throw reason })
    );
  }

  static resolve(value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
  }

  static reject(reason) {
    return new MyPromise((_, reject) => reject(reason));
  }

  static all(promises) {
    return new MyPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError('Argument must be an array'));
      }
      const results = [];
      let count = 0;
      if (promises.length === 0) return resolve(results);

      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          value => {
            results[index] = value;
            count++;
            if (count === promises.length) resolve(results);
          },
          reject
        );
      });
    });
  }

  static race(promises) {
    return new MyPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError('Argument must be an array'));
      }
      promises.forEach(promise => {
        MyPromise.resolve(promise).then(resolve, reject);
      });
    });
  }

  static allSettled(promises) {
    return new MyPromise((resolve) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError('Argument must be an array'));
      }
      const results = [];
      let count = 0;
      if (promises.length === 0) return resolve(results);

      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          value => {
            results[index] = { status: 'fulfilled', value };
            count++;
            if (count === promises.length) resolve(results);
          },
          reason => {
            results[index] = { status: 'rejected', reason };
            count++;
            if (count === promises.length) resolve(results);
          }
        );
      });
    });
  }

  static any(promises) {
    return new MyPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError('Argument must be an array'));
      }
      const errors = [];
      let count = 0;
      if (promises.length === 0) {
        return reject(new AggregateError([], 'All promises were rejected'));
      }

      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          resolve,
          reason => {
            errors[index] = reason;
            count++;
            if (count === promises.length) {
              reject(new AggregateError(errors, 'All promises were rejected'));
            }
          }
        );
      });
    });
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise'));
  }

  if (x instanceof MyPromise) {
    x.then(resolve, reject);
    return;
  }

  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    let called = false;
    try {
      const then = x.then;
      if (typeof then === 'function') {
        then.call(
          x,
          y => {
            if (called) return;
            called = true;
            resolvePromise(promise2, y, resolve, reject);
          },
          r => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        resolve(x);
      }
    } catch (error) {
      if (called) return;
      called = true;
      reject(error);
    }
  } else {
    resolve(x);
  }
}

// ==================== 测试用例 ====================

console.log('=== 测试1: 基本功能 ===');
new MyPromise((resolve) => {
  setTimeout(() => resolve('成功'), 100);
}).then(value => console.log('结果:', value));

console.log('\n=== 测试2: 链式调用 ===');
MyPromise.resolve(1)
  .then(value => value + 1)
  .then(value => value * 2)
  .then(value => console.log('链式结果:', value));

console.log('\n=== 测试3: 错误处理 ===');
MyPromise.reject('错误')
  .catch(error => console.log('捕获到:', error));

console.log('\n=== 测试4: Promise.all ===');
MyPromise.all([
  MyPromise.resolve(1),
  MyPromise.resolve(2),
  MyPromise.resolve(3)
]).then(values => console.log('all 结果:', values));

console.log('\n=== 测试5: Promise.race ===');
MyPromise.race([
  new MyPromise(resolve => setTimeout(() => resolve('慢'), 1000)),
  new MyPromise(resolve => setTimeout(() => resolve('快'), 100))
]).then(value => console.log('race 结果:', value));

🎯 学习建议:

  1. 理解原理: 先理解 Promise 的状态机制和执行流程
  2. 动手实现: 从简单版本开始,逐步完善功能
  3. 调试测试: 运行代码,观察输出,理解每一步
  4. 应用实践: 在实际项目中使用 Promise 解决问题
  5. 面试准备: 能够清晰解释原理,流畅手写代码

💡 记住这句话:

Promise 的核心是状态管理 + 回调队列 + 链式调用,掌握这三点就掌握了 Promise 的精髓!

祝你面试顺利,工作游刃有余!🚀