聊天讨论 面试官让我手写 Promise,我打开 Cursor 三秒生成,他愣了两秒说 “你过了”

193577746(kyriewen) · May 19, 2026 · 34 hits

上个月面了一家中厂,技术面第二轮,面试官笑眯眯地说:“来,手写一个 Promise。” 我脑子嗡了一下——这题我背过,但那是三年前。真要默写,肯定漏一堆边界。我看了他一眼,问:“可以用 AI 吗?” 他愣了一下,说:“你试试。” 我打开 Cursor,对着 Composer 说:“帮我实现一个符合 Promise/A+ 规范的 Promise,包含 then、catch、finally。” 三秒后代码生成。他看了两秒,说:“你过了。”

前言

手写 Promise,面试经典老题。但 2026 年了,还有多少人在面试前夜死磕resolverejectthen的链式调用?我不是说这东西不该学——理解原理很重要。但面试时要你一字不差默写出来,意义在哪?工作中你真的会自己写一个 Promise 吗?不会,你用原生或者蓝鸟。

这周我面了三家公司,两家允许用 AI 辅助编码,一家连 Stack Overflow 都不让开。结果呢?允许 AI 的那两家我拿到了 offer,不让的那家我连二面都没进。不是因为我不会写 Promise,而是因为他们考察的还是五年前的能力模型

今天我就把那场面试的完整过程复盘给你:我是怎么用 Cursor 生成标准 Promise 实现的,面试官为什么认可,以及如果面试官不让你用 AI,你应该怎么回应。最后附一份可以直接复制的手写 Promise 代码(带详细注释),你拿去背也行,拿去让 AI 生成也行。

一、为什么 “手写 Promise” 还是一道高频题?

这题活了快十年了。从 ES6 诞生到现在,面前端必问。面试官想考察的点其实不是你会不会用 Promise,而是:

  • 你对异步编程的理解深度(微任务、状态流转、链式调用)
  • 你代码的健壮性(边界处理、错误冒泡、值穿透)
  • 你是否理解 Promise/A+ 规范(而不是只背了个大概)

但问题是,这些能力真的需要默写几百行代码来验证吗? 一个能讲清楚 Promise 原理、能说对 then 的返回值为什么是新的 Promise、能解释微任务队列顺序的候选人,即使写代码时借助了 AI,他也合格了。

二、面试现场:我是怎么用 Cursor“作弊” 的

面试官出了题,我没有立刻敲。我说:“我平时主力工具是 Cursor,我可以用它辅助编码吗?我可以当场解释每一行代码的作用。” 他犹豫了一下,说:“那你试试,但你要讲清楚。”

我打开 Cursor 的 Composer(快捷键 Cmd+K),输入:

实现一个符合 Promise/A+ 规范的 Promise 类。要求:状态只能从 pending 变为 fulfilled 或 rejected,且不可逆;then 方法返回一个新的 Promise;支持链式调用;支持值穿透;支持异步执行;错误捕获。

三秒后,Cursor 生成了完整代码。我一行一行给面试官解释:

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

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

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

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

  static all(promises) {
    return new MyPromise((resolve, reject) => {
      const results = [];
      let count = 0;
      if (promises.length === 0) return resolve(results);
      promises.forEach((p, idx) => {
        MyPromise.resolve(p).then(
          val => {
            results[idx] = val;
            count++;
            if (count === promises.length) resolve(results);
          },
          reject
        );
      });
    });
  }

  static race(promises) {
    return new MyPromise((resolve, reject) => {
      promises.forEach(p => {
        MyPromise.resolve(p).then(resolve, reject);
      });
    });
  }
}

// 辅助函数:处理then返回值
function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected'));
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject);
    return;
  }
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    let then;
    try {
      then = x.then;
    } catch (err) {
      return reject(err);
    }
    if (typeof then === 'function') {
      let called = false;
      try {
        then.call(
          x,
          y => {
            if (called) return;
            called = true;
            resolvePromise(promise2, y, resolve, reject);
          },
          r => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } catch (err) {
        if (called) return;
        reject(err);
      }
    } else {
      resolve(x);
    }
  } else {
    resolve(x);
  }
}

面试官听完,问了我两个问题:

  1. “为什么 then 里面要用 queueMicrotask?” —— 为了模拟原生 Promise 的微任务行为。
  2. “resolvePromise 里为什么要判断循环引用?” —— 防止const p = new Promise((resolve) => { resolve(p); })这类死循环。

我答出来了。他点了点头,没有继续问。

金句:面试官让你手写 Promise,不是要你默写 API,而是看你知不知道 “为什么要这么写”。

三、面试官为什么认可我用 AI?

我把代码解释清楚后,面试官说了一句话:“你能讲明白,说明你懂原理。工具只是手段,不是目的。”

这个时代,会背代码已经不值钱了。AI 30 秒就能生成一个标准 Promise 实现。真正的能力是:

  • 你能不能判断 AI 生成的代码对不对?
  • 你能不能优化它(比如去掉冗余逻辑、调整性能)?
  • 你能不能把它集成到更大的系统里?

所以,如果你下次面试遇到 “手写 XXX”,大胆问:“我用 AI 辅助可以吗?我保证每一行都能解释清楚。” 大部分开明的面试官会同意,甚至会更欣赏你——因为你展示了真实的工作方式,而不是应试技巧。

四、如果面试官不让用 AI,怎么办?

也简单。你告诉他:我可以手写关键结构,但完整实现需要很多边界处理代码。要不我写核心流程,再口述其他部分?

然后你快速写出骨架:构造函数 + resolve/reject + then 的基本逻辑(省略 resolvePromise 里的细节)。面试官通常不会真让你写全,你展示出理解就够了。

千万不要硬背代码。背错了比不会更尴尬。

五、实测数据:手写 Promise 到底有多长?

我统计了一下:

  • 符合 Promise/A+ 规范的标准实现(含静态方法):约 150-200 行
  • 手写完整代码(不含注释),熟练开发者需要 15-20 分钟
  • 用 Cursor 生成 + 人工 review:3 分钟生成,5 分钟 review

你在面试中愿意花 20 分钟默写,还是花 8 分钟解释原理 + 让 AI 生成?

六、注意事项(坑点)

  • 如果你用 AI 生成,一定要能解释每一段的作用。面试官随时会打断问:“为什么这里有 queueMicrotask?”“为什么 then 要返回新 Promise?” 答不上来,就是减分项。
  • AI 生成的代码可能不符合你公司的命名风格。没关系,手动改一下变量名。
  • 不要完全依赖 AI。至少自己手写过一两次,理解核心难点(比如状态流转、微任务队列、值穿透)。

七、写在最后

我最终拿到了那家公司的 offer。入职后我问面试官,当时为什么同意我用 AI?他说:“因为我们团队每天都在用 Cursor。招一个不会用 AI 的人进来,反而是累赘。”

2026 年的前端面试,已经不是在考 “你会不会写”,而是在考 “你会不会用工具写”。手写 Promise 仍然是一道好题,但考核的重点已经变了。如果你还在靠死记硬背准备面试,可能会越来越吃力。

你在面试中用 AI 工具被质疑过吗?后来怎么解释的?点个赞让我看到有多少人偷偷用过。

No Reply at the moment.
You need to Sign in before reply, if you don't have an account, please Sign up first.