Skip to content

高级实现手写题

掌握 Promise 完整实现和事件发布订阅模式

7. Promise 完整实现

难度: ⭐⭐⭐ | 梯队: 进阶 | 标签: 状态机, 微任务

题目描述

实现一个符合 Promises/A+ 规范的 Promise。

代码实现

javascript
/**
 * Promise 完整实现
 */
class MyPromise {
  static PENDING = 'pending';
  static FULFILLED = 'fulfilled';
  static REJECTED = 'rejected';

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

    const resolve = (value) => {
      if (this.status === MyPromise.PENDING) {
        this.status = MyPromise.FULFILLED;
        this.value = value;
        this.onFulfilledCallbacks.forEach((fn) => fn());
      }
    };

    const reject = (reason) => {
      if (this.status === MyPromise.PENDING) {
        this.status = 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);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      };

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

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

    return promise2;
  }

  /**
   * 处理 then 返回值
   */
  resolvePromise(promise2, x, resolve, reject) {
    // 防止循环引用
    if (promise2 === x) {
      return reject(new TypeError('Chaining cycle detected'));
    }

    if (x instanceof MyPromise) {
      x.then(resolve, reject);
    } else 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;
              this.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);
    }
  }

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

  finally(callback) {
    return this.then(
      (value) => MyPromise.resolve(callback()).then(() => value),
      (reason) => MyPromise.resolve(callback()).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));
  }
}

示例调用

javascript
// 基础用法
new MyPromise((resolve) => {
  resolve(1);
}).then((value) => {
  console.log(value); // 1
  return value + 1;
}).then((value) => {
  console.log(value); // 2
});

// 边界条件:异步 resolve
new MyPromise((resolve) => {
  setTimeout(() => resolve('async'), 100);
}).then((value) => {
  console.log(value); // 'async'
});

// 边界条件:链式调用返回 Promise
new MyPromise((resolve) => {
  resolve(1);
}).then((value) => {
  return new MyPromise((r) => r(value * 2));
}).then((value) => {
  console.log(value); // 2
});

// 边界条件:错误处理
new MyPromise((_, reject) => {
  reject('error');
}).catch((reason) => {
  console.log(reason); // 'error'
});

// 边界条件:then 中抛出错误
new MyPromise((resolve) => {
  resolve(1);
}).then(() => {
  throw new Error('oops');
}).catch((err) => {
  console.log(err.message); // 'oops'
});

// 边界条件:finally
new MyPromise((resolve) => {
  resolve(1);
}).finally(() => {
  console.log('cleanup'); // 'cleanup'
}).then((value) => {
  console.log(value); // 1
});

复杂度分析

  • 时间: O(1) 单次操作
  • 空间: O(n) 回调队列

8. 事件发布订阅

难度: ⭐⭐ | 梯队: 第三梯队 | 标签: 设计模式, EventEmitter

题目描述

实现一个事件发布订阅系统,支持 on、off、emit、once 方法。

代码实现

javascript
/**
 * EventEmitter 事件发布订阅
 */
class EventEmitter {
  constructor() {
    this.events = new Map();
  }

  /**
   * 订阅事件
   * @param {string} event - 事件名
   * @param {Function} listener - 监听函数
   * @return {EventEmitter}
   */
  on(event, listener) {
    if (!this.events.has(event)) {
      this.events.set(event, []);
    }
    this.events.get(event).push(listener);
    return this;
  }

  /**
   * 取消订阅
   * @param {string} event - 事件名
   * @param {Function} listener - 要移除的监听函数
   * @return {EventEmitter}
   */
  off(event, listener) {
    if (!this.events.has(event)) return this;

    if (!listener) {
      // 没有指定 listener,移除该事件所有监听
      this.events.delete(event);
    } else {
      const listeners = this.events.get(event);
      const index = listeners.indexOf(listener);
      if (index !== -1) {
        listeners.splice(index, 1);
      }
      if (listeners.length === 0) {
        this.events.delete(event);
      }
    }

    return this;
  }

  /**
   * 触发事件
   * @param {string} event - 事件名
   * @param {...any} args - 传递给监听函数的参数
   * @return {boolean}
   */
  emit(event, ...args) {
    if (!this.events.has(event)) return false;

    const listeners = this.events.get(event).slice(); // 复制防止修改
    listeners.forEach((listener) => {
      listener.apply(this, args);
    });

    return true;
  }

  /**
   * 只订阅一次
   * @param {string} event - 事件名
   * @param {Function} listener - 监听函数
   * @return {EventEmitter}
   */
  once(event, listener) {
    const wrapper = (...args) => {
      listener.apply(this, args);
      this.off(event, wrapper);
    };
    // 保存原始函数引用,方便 off 时匹配
    wrapper.originalListener = listener;

    this.on(event, wrapper);
    return this;
  }

  /**
   * 获取事件的监听器数量
   */
  listenerCount(event) {
    return this.events.has(event) ? this.events.get(event).length : 0;
  }

  /**
   * 移除所有监听器
   */
  removeAllListeners(event) {
    if (event) {
      this.events.delete(event);
    } else {
      this.events.clear();
    }
    return this;
  }
}

示例调用

javascript
const emitter = new EventEmitter();

// 基础用法:on 和 emit
const handler = (msg) => console.log(`Received: ${msg}`);
emitter.on('message', handler);
emitter.emit('message', 'Hello'); // 'Received: Hello'

// 多个监听器
emitter.on('message', (msg) => console.log(`Also got: ${msg}`));
emitter.emit('message', 'World');
// 'Received: World'
// 'Also got: World'

// off 移除监听器
emitter.off('message', handler);
emitter.emit('message', 'Test');
// 只输出 'Also got: Test'

// once 只执行一次
emitter.once('init', () => console.log('Initialized!'));
emitter.emit('init'); // 'Initialized!'
emitter.emit('init'); // 无输出

// 边界条件:emit 不存在的事件
console.log(emitter.emit('unknown')); // false

// 边界条件:off 不存在的事件
emitter.off('nonexistent'); // 不报错

// 边界条件:多参数
emitter.on('data', (a, b, c) => console.log(a, b, c));
emitter.emit('data', 1, 2, 3); // 1 2 3

// 边界条件:链式调用
new EventEmitter()
  .on('a', () => console.log('a'))
  .on('b', () => console.log('b'))
  .emit('a')
  .emit('b');

// 边界条件:listenerCount
const ee = new EventEmitter();
ee.on('test', () => {});
ee.on('test', () => {});
console.log(ee.listenerCount('test')); // 2

// 边界条件:removeAllListeners
ee.removeAllListeners('test');
console.log(ee.listenerCount('test')); // 0

复杂度分析

  • 时间:
    • on/off: O(n) n 为该事件的监听器数
    • emit: O(n)
    • once: O(1)
  • 空间: O(m × n),m 为事件数,n 为平均监听器数

前端面试知识库