上個月面了一家中型公司,技術面第二輪,面試官笑咪咪地說:「來,手寫一個 Promise。」我腦子嗡了一下——這題我背過,但那是三年前。真要默寫,肯定漏一堆邊界。我看了他一眼,問:「可以用 AI 嗎?」他愣了一下,說:「你試試。」我打開 Cursor,對著 Composer 說:「幫我實作一個符合 Promise/A+ 規範的 Promise,包含 then、catch、finally。」三秒後程式碼生成。他看了兩秒,說:「你過了。」
手寫 Promise,面試經典老題。但 2026 年了,還有多少人在面試前夜死磕 resolve、reject、then 的鏈式呼叫?我不是說這東西不該學——理解原理很重要。但面試時要你一字不差默寫出來,意義在哪?工作中你真的會自己寫一個 Promise 嗎?不會,你用原生的或 Bluebird。
這週我面了三家公司,兩家允許用 AI 輔助寫程式,一家連 Stack Overflow 都不讓開。結果呢?允許 AI 的那兩家我拿到了 offer,不讓的那家我連二面都沒進。不是因為我不會寫 Promise,而是因為他們考察的還是五年前的能力模型。
今天我就把那場面試的完整過程復盤給你:我是怎麼用 Cursor 生成標準 Promise 實作的,面試官為什麼認可,以及如果面試官不讓你用 AI,你應該怎麼回應。最後附一份可以直接複製的手寫 Promise 程式碼(帶詳細註解),你拿去背也行,拿去讓 AI 生成也行。
這題活了快十年了。從 ES6 誕生到現在,面前端必問。面試官想考察的點其實不是你會不會用 Promise,而是:
但問題是,這些能力真的需要默寫幾百行程式碼來驗證嗎? 一個能講清楚 Promise 原理、能說對 then 的回傳值為什麼是新的 Promise、能解釋微任務佇列順序的候選人,即使寫程式時借助了 AI,他也合格了。
面試官出了題,我沒有立刻敲。我說:「我平時主力工具是 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);
}
}
面試官聽完,問了我兩個問題:
const p = new Promise((resolve) => { resolve(p); }) 這類死循環。我答出來了。他點了點頭,沒有繼續問。
金句:面試官叫你手寫 Promise,不是要你默寫 API,而是看你知不知道「為什麼要這樣寫」。
我把程式碼解釋清楚後,面試官說了一句話:「你能講明白,說明你懂原理。工具只是手段,不是目的。」
這個時代,會背程式碼已經不值錢了。AI 30 秒就能生成一個標準 Promise 實作。真正的能力是:
所以,如果你下次面試遇到「手寫 XXX」,大膽問:「我用 AI 輔助可以嗎?我保證每一行都能解釋清楚。」大部分開明的面試官會同意,甚至會更欣賞你——因為你展示了真實的工作方式,而不是應試技巧。
也簡單。你告訴他:我可以手寫關鍵結構,但完整實作需要很多邊界處理程式碼。要不我寫核心流程,再口述其他部分?
然後你快速寫出骨架:建構子 + resolve/reject + then 的基本邏輯(省略 resolvePromise 裡的細節)。面試官通常不會真的讓你寫全,你展示出理解就夠了。
千萬不要硬背程式碼。背錯了比不會更尷尬。
我統計了一下:
你在面試中願意花 20 分鐘默寫,還是花 8 分鐘解釋原理 + 讓 AI 生成?
我最終拿到了那家公司的 offer。入職後我問面試官,當時為什麼同意我用 AI?他說:「因為我們團隊每天都在用 Cursor。招一個不會用 AI 的人進來,反而是累贅。」
2026 年的前端面試,已經不是在考「你會不會寫」,而是在考「你會不會用工具寫」。手寫 Promise 仍然是一道好題,但考核的重點已經變了。如果你還在靠死記硬背準備面試,可能會越來越吃力。
你在面試中用 AI 工具被質疑過嗎?後來怎麼解釋的?點個讚讓我看到有多少人偷偷用過。