Promise的三種狀態
1. 狀態定義
Promise對象代表一個異步操作的最終完成或失敗,它有且只有三種互斥的狀態:
- Pending(進行中)
- 初始狀態,既不是成功也不是失敗
- 操作正在進行中,尚未完成
- Fulfilled(已成功)
- 異步操作成功完成
- 也稱為"resolved"(已解決)
- 狀態一旦確定,不可再改變
- Rejected(已失敗)
- 異步操作失敗
- 狀態一旦確定,不可再改變
2. 狀態轉換機制
初始狀態:Pending
↓
┌──成功執行─────→ Fulfilled
│ (調用resolve)
Pending
│ (調用reject或拋出異常)
└──失敗執行─────→ Rejected
重要特性:
1. 單向流動:只能從Pending → Fulfilled 或 Pending → Rejected
2. 不可逆:一旦轉為Fulfilled或Rejected,狀態凝固不再改變
3. 外部不可變:狀態只能由Promise內部改變,外部只能讀取
3. 狀態特點
- 確定即凝固:狀態變化是一次性的
- 不可取消:一旦開始執行,無法取消(但可以通過其他方式模擬)
- 異步傳播:狀態變化會異步通知所有監聽者(then/catch)
then方法的鏈式調用原理
1. 鏈式調用的核心機制
每個then()方法都返回一個全新的Promise對象,這是鏈式調用的基礎。
原始Promise → then() → 新Promise1 → then() → 新Promise2 → ...
2. 執行流程規則
promise
.then(onFulfilled1, onRejected1) // 返回新Promise1
.then(onFulfilled2, onRejected2) // 返回新Promise2
.then(onFulfilled3, onRejected3) // 返回新Promise3
規則1:回調函數執行時機
- 當前一個Promise狀態確定後,才執行對應的回調
- 回調函數總是異步執行(微任務)
規則2:新Promise的狀態決定
新Promise的狀態由回調函數的返回值或執行過程決定:
|
回調函數情況 |
新Promise狀態 |
值/原因 |
|
返回普通值 |
Fulfilled |
該值 |
|
返回Promise |
跟隨該Promise |
跟隨該Promise |
|
拋出異常 |
Rejected |
拋出的異常 |
|
無返回值 |
Fulfilled |
undefined |
規則3:狀態傳遞機制
如果then沒有提供對應的回調函數,狀態會穿透到鏈中的下一個Promise:
// 狀態穿透示例
Promise.reject('error')
.then(null, null) // 無處理函數,穿透
.then(onFulfilled, null) // 無reject處理,穿透
.then(null, onRejected) // 這裏捕獲錯誤
3. 具體運行原理
3.1 成功鏈(Fulfilled鏈)
Promise.resolve(1)
.then(x => x + 1) // 返回2 → 新Promise狀態Fulfilled
.then(x => x * 2) // 返回4 → 新Promise狀態Fulfilled
.then(console.log) // 輸出4
3.2 錯誤捕獲與恢復
Promise.reject('error1')
.catch(err => {
console.log('捕獲:', err); // 輸出:捕獲: error1
return '恢復值'; // 返回普通值 → 鏈恢復為Fulfilled
})
.then(val => {
console.log('恢復後:', val); // 輸出:恢復後: 恢復值
})
3.3 Promise返回的特殊處理
Promise.resolve(1)
.then(x => {
return new Promise(resolve => {
setTimeout(() => resolve(x + 1), 1000);
}); // 返回Promise,新Promise等待這個Promise解決
})
.then(x => {
console.log(x); // 2(1秒後執行)
});
4. 內部實現簡化原理
// then方法的簡化實現(原理示意)
class MyPromise {
constructor(executor) { /* ... */ }
then(onFulfilled, onRejected) {
// 1. 創建並返回新的Promise
return new MyPromise((resolve, reject) => {
// 2. 封裝回調函數
const handleCallback = () => {
try {
// 3. 獲取當前Promise的結果
const result = this.state === 'fulfilled'
? (onFulfilled ? onFulfilled(this.value) : this.value)
: (onRejected ? onRejected(this.reason) : this.reason);
// 4. 根據回調返回值決定新Promise狀態
if (result instanceof MyPromise) {
// 如果返回Promise,等待它的結果
result.then(resolve, reject);
} else {
// 返回普通值,新Promise成功
resolve(result);
}
} catch (error) {
// 5. 回調拋出異常,新Promise失敗
reject(error);
}
};
// 6. 異步執行(微任務)
if (this.state === 'pending') {
this.callbacks.push(handleCallback);
} else {
queueMicrotask(handleCallback);
}
});
}
}
5. 鏈式調用的關鍵特性
5.1 異步執行保證
then/catch/finally回調總是異步執行- 即使Promise已是確定狀態,回調也放入微任務隊列
// 執行順序示例
console.log('1');
Promise.resolve()
.then(() => console.log('3'))
.then(() => console.log('4'));
console.log('2');
// 輸出順序:1 → 2 → 3 → 4
5.2 錯誤傳播機制
Promise鏈中的錯誤會一直向後傳遞,直到被捕獲
↓ 拋出錯誤
Promise1 → Promise2 → Promise3 → catch
狀態:Rejected 穿透 穿透 捕獲
5.3 返回值處理
// 返回值影響鏈的繼續
Promise.resolve()
.then(() => {
// 情況1:返回普通值,繼續成功鏈
return 42;
})
.then(value => {
console.log(value); // 42
// 情況2:返回Promise,等待它
return new Promise(resolve => {
setTimeout(() => resolve('done'), 1000);
});
})
.then(value => {
console.log(value); // 'done'(1秒後)
// 情況3:拋出異常,轉為失敗鏈
throw new Error('失敗');
})
.catch(error => {
console.error(error); // 捕獲錯誤
// 情況4:catch中返回值,恢復成功鏈
return '恢復';
})
.then(value => {
console.log(value); // '恢復'
});
6. 實際應用場景
場景1:順序異步操作
// 用户登錄流程
login(userInfo)
.then(token => getUserData(token)) // 獲取用户數據
.then(userData => getPermissions(userData)) // 獲取權限
.then(perms => initApp(perms)) // 初始化應用
.catch(error => showError(error)); // 統一錯誤處理
場景2:並行與順序結合
// 先並行請求,再順序處理
Promise.all([fetchData1(), fetchData2()])
.then(([data1, data2]) => {
// 並行完成後順序處理
return processData1(data1)
.then(result1 => processData2(result1, data2));
})
.then(finalResult => {
// 最終處理
});
場景3:超時控制
function withTimeout(promise, timeout) {
return Promise.race([
promise,
new Promise((_, reject) => {
setTimeout(() => reject(new Error('超時')), timeout);
})
]);
}
withTimeout(fetchData(), 5000)
.then(data => console.log(data))
.catch(error => console.error('失敗:', error));
7. 常見陷阱與注意事項
陷阱1:忘記返回
// ❌ 錯誤:忘記return,後續then接收undefined
someAsync()
.then(result => {
process(result); // 忘記return
})
.then(nextResult => {
// nextResult 是 undefined
});
// ✅ 正確:總是返回需要的值
someAsync()
.then(result => {
return process(result); // 顯式return
});
陷阱2:錯誤處理不當
// ❌ 錯誤:catch位置不當,無法捕獲前面的錯誤
someAsync()
.catch(error => handleError(error)) // 只捕獲someAsync的錯誤
.then(nextAsync) // nextAsync的錯誤會逃逸
.then(anotherAsync); // 這裏的錯誤無捕獲
// ✅ 正確:在鏈末端統一捕獲
someAsync()
.then(nextAsync)
.then(anotherAsync)
.catch(error => handleAllErrors(error)); // 捕獲所有錯誤
陷阱3:微任務阻塞
// 避免在微任務中執行耗時操作
Promise.resolve()
.then(() => {
// ❌ 避免:長時間同步操作阻塞微任務隊列
heavyComputation(); // 阻塞後續所有微任務
// ✅ 改為:將耗時任務放入宏任務
return new Promise(resolve => {
setTimeout(() => {
heavyComputation();
resolve();
}, 0);
});
});
8. 與async/await的關係
async函數本質
// async函數自動返回Promise
async function foo() {
return 42;
// 等價於:return Promise.resolve(42);
}
// await本質是then的語法糖
async function bar() {
const result = await somePromise();
// 等價於:somePromise().then(result => { ... })
const next = await anotherPromise();
return next;
}
鏈式調用轉async/await
// Promise鏈
fetchData()
.then(process)
.then(save)
.then(log)
.catch(handleError);
// 等價async/await
async function handle() {
try {
const data = await fetchData();
const processed = await process(data);
const saved = await save(processed);
await log(saved);
} catch (error) {
handleError(error);
}
}
總結要點
Promise狀態核心
- 三種狀態:Pending、Fulfilled、Rejected
- 單向轉換:一旦確定,不可逆轉
- 異步通知:狀態變化通過微任務異步通知
鏈式調用本質
- 新Promise生成:每個then都創建新Promise,實現鏈式
- 狀態傳遞規則:
- 有回調:按回調返回值決定新狀態
- 無回調:狀態值穿透到下一環
- 錯誤傳播:錯誤沿鏈向後傳遞,直到被捕獲
Promise的鏈式調用是異步編程的基礎,理解其原理有助於編寫更清晰、健壯的異步代碼。在現代JavaScript中,雖然async/await提供了更直觀的語法,但其底層仍然是基於Promise的鏈式調用機制。