面試官叫我手寫 Promise,我打開 Cursor 三秒生成,他愣了兩秒說「你過了」

上個月面了一家中型公司,技術面第二輪,面試官笑咪咪地說:「來,手寫一個 Promise。」我腦子嗡了一下——這題我背過,但那是三年前。真要默寫,肯定漏一堆邊界。我看了他一眼,問:「可以用 AI 嗎?」他愣了一下,說:「你試試。」我打開 Cursor,對著 Composer 說:「幫我實作一個符合 Promise/A+ 規範的 Promise,包含 then、catch、finally。」三秒後程式碼生成。他看了兩秒,說:「你過了。」

前言

手寫 Promise,面試經典老題。但 2026 年了,還有多少人在面試前夜死磕 resolverejectthen 的鏈式呼叫?我不是說這東西不該學——理解原理很重要。但面試時要你一字不差默寫出來,意義在哪?工作中你真的會自己寫一個 Promise 嗎?不會,你用原生的或 Bluebird。

這週我面了三家公司,兩家允許用 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 体验AI代码助手 代码解读复制代码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 工具被質疑過嗎?後來怎麼解釋的?點個讚讓我看到有多少人偷偷用過。


原文出處:https://juejin.cn/post/7641230849086668800


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝12   💬4   ❤️1
464
🥈
alicec
📝1   ❤️2
87
#4
我愛JS
💬1  
3
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登