2026 年 6 月 10 日 星期三
  • 登录
  • 注册
周天财经
广告
  • 首页
  • 24 小时
  • 世界
  • 商业
  • 基金
  • 期货
  • 股票
  • 行业新闻
  • 黄金
没有结果
查看所有结果
  • 首页
  • 24 小时
  • 世界
  • 商业
  • 基金
  • 期货
  • 股票
  • 行业新闻
  • 黄金
没有结果
查看所有结果
周天财经
没有结果
查看所有结果
首页 基金

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

2026 年 5 月 20 日
在 基金
阅读时间: 14 mins read
阅读:1068
A A


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

Related articles

收评:创业板指大涨近 4%,半导体、PCB 全线爆发,沪指重返 4000 点

2026 年 6 月 10 日

政策量产双重加持,机器人产业链持续升温,机器人 ETF 易方达 (159530) 今日获超 1 亿份净申购

2026 年 6 月 10 日

前言

手写 Promise,面试经典老题。但 2026 年了,还有多少人在面试前夜死磕 resolve、reject、then 的链式调用?我不是说这东西不该学——理解原理很重要。但面试时要你一字不差默写出来,意义在哪?工作中你真的会自己写一个 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 工具被质疑过吗?后来怎么解释的?点个赞让我看到有多少人偷偷用过。

相关 文章

收评:创业板指大涨近 4%,半导体、PCB 全线爆发,沪指重返 4000 点

来自 周天财经
2026 年 6 月 10 日
0

6 月 9 日,A 股今日迎来全面反弹行情,三大...

政策量产双重加持,机器人产业链持续升温,机器人 ETF 易方达 (159530) 今日获超 1 亿份净申购

来自 周天财经
2026 年 6 月 10 日
0

截至收盘,国证机器人产业指数上涨 0.7%...

我做了个桌面提词器,在屏幕共享或录屏中不可见

我做了个桌面提词器,在屏幕共享或录屏中不可见

来自 周天财经
2026 年 6 月 10 日
0

最近在折腾一个叫 FlowPrompte...

粮食 ETF(159033) 跟踪

来自 周天财经
2026 年 6 月 9 日
0

上市时间:2026-06-05

分红季 | 每股现金红利人民币 0.11755 元!红利低波 (H30269.CSI) 成分股渝农商行,发布 2025 年年度 A 股权益分派实施公告!

来自 周天财经
2026 年 6 月 9 日
0

26 年 6 月 9 日,渝农商行发布 《重庆农村商...

加载更多
广告
  • 热门
  • 评论
  • 最新
神马经典投研: 集资讯、策略、研报一站式期货投研工具

神马经典投研: 集资讯、策略、研报一站式期货投研工具

2025 年 11 月 7 日
「我们也深陷残酷价格战」,德资巨头中国区高管警告

「我们也深陷残酷价格战」,德资巨头中国区高管警告

2025 年 8 月 4 日
一周产业基金|上海市人工智能CVC基金发布;湖北百亿人形机器人母基金来了

一周产业基金|上海市人工智能 CVC 基金发布;湖北百亿人形机器人母基金来了

2025 年 8 月 4 日
「硬科技」指数携手上涨,半导体设备ETF易方达(159558)、芯片ETF易方达(516350)等产品助力布局板块龙头

基民懵了!这个火爆的板块年内涨超 37%,主力却借道 ETF 狂抛逾 400 亿元

2025 年 9 月 20 日
Lesson 1: Basics Of Photography With Natural Lighting

The Single Most Important Thing You Need To Know About Success

4
Lesson 1: Basics Of Photography With Natural Lighting

Lesson 1: Basics Of Photography With Natural Lighting

3
Lesson 1: Basics Of Photography With Natural Lighting

5 Ways Animals Will Help You Get More Business

2
Lesson 1: Basics Of Photography With Natural Lighting

New Cryptocurrency That Will Kill Of Bitcoin

2
商品日报(6月9日):焦煤盘中触及跌停 地缘局势缓和油价回落

商品日报 (6 月 9 日):焦煤盘中触及跌停 地缘局势缓和油价回落

2026 年 6 月 10 日
「中国的亚马逊」如何跌落?李国庆自曝9拒刘强东,京东价格战打垮当当

「中国的亚马逊」 如何跌落?李国庆自曝 9 拒刘强东,京东价格战打垮当当

2026 年 6 月 10 日

沪指重回 4000 点,科技股大涨,非科技股也 「罕见」 涨了!有三大原因

2026 年 6 月 10 日

收评:创业板指大涨近 4%,半导体、PCB 全线爆发,沪指重返 4000 点

2026 年 6 月 10 日
  • 隐私政策
  • 联系我们
  • 关于周天
  • 登录
  • 注册
投诉建议:+86 13326565461

© 2025 广州小舟天传媒有限公司 by 周天财经 - 粤 ICP 备 2025452169 号-1

没有结果
查看所有结果
  • 首页
  • 24 小时
  • 世界
  • 商业
  • 基金
  • 期货
  • 股票
  • 行业新闻
  • 黄金

© 2025 广州小舟天传媒有限公司 by 周天财经 - 粤 ICP 备 2025452169 号-1

欢迎回来!

在下面登录您的帐户

忘记密码? 注册

创建新帐户!

填写以下表格进行注册

所有项目需要填写。 登录

重置您的密码

请输入您的用户名或电子邮件地址以重置密码。

登录

用户登录

还没有账号?立即注册

用户注册

已有账号?立即登录