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

上个月面了一家中厂,技术面第二轮,面试官笑眯眯地说:"来,手写一个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生成了完整代码。我一行一行给面试官解释:

js 复制代码
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工具被质疑过吗?后来怎么解释的?点个赞让我看到有多少人偷偷用过。

相关推荐
Bacon6 小时前
RAG 从入门到入土:Agent 时代,你的检索增强生成到底行不行?
前端·人工智能
软件开发技术深度爱好者6 小时前
HTML实现DOCX文档版题库图文考试系统(修订)
前端·javascript·html
宁雨桥6 小时前
从跨项目预览到分层架构:一次 `postMessage` 封装的深度思考
前端·架构·postmessage
蝎子莱莱爱打怪6 小时前
我花两年业余时间做了个IM系统,然后呢😂??
后端·flutter·面试
问征夫以前路6 小时前
Promise知识点回顾
前端·javascript
行走的陀螺仪6 小时前
JavaScript 算法详解:10大经典算法,通俗易懂,从入门到精通
开发语言·javascript·算法
努力成为AK大王7 小时前
Java并发线程核心知识(一)
java·开发语言·面试
拓荒牛儿7 小时前
前端内存可观测实践
前端
yqcoder7 小时前
异步的魔法:深入解析 async/await 原理与编译本质
前端·javascript