JavaScript?Promise錯(cuò)誤處理與最佳實(shí)踐
一句話概括:Promise 是 ES6 引入的異步編程解決方案,用于表示一個(gè)異步操作的最終完成或失敗,以及其返回的值。它解決了“回調(diào)地獄”(Callback Hell)問(wèn)題,使異步代碼更清晰、可讀、可維護(hù)。
一、為什么需要Promise?(歷史背景)
在 Promise 出現(xiàn)之前,JavaScript 主要通過(guò) 回調(diào)函數(shù)(Callback) 處理異步操作:
// 回調(diào)地獄(Callback Hell)
doTask1((err, result1) => {
if (err) return handleError(err);
doTask2(result1, (err, result2) => {
if (err) return handleError(err);
doTask3(result2, (err, result3) => {
if (err) return handleError(err);
console.log('完成:', result3);
});
});
});
回調(diào)函數(shù)的痛點(diǎn):
- 嵌套層級(jí)深,難以閱讀
- 錯(cuò)誤處理重復(fù)
- 無(wú)法
return或throw - 控制流復(fù)雜(并行、串行、競(jìng)態(tài)等)
Promise 的出現(xiàn)就是為了解決這些問(wèn)題。
二、Promise的三種狀態(tài)(States)
Promise 對(duì)象有且僅有三種狀態(tài),一旦改變不可逆:
| 狀態(tài) | 說(shuō)明 |
|---|---|
pending | 初始狀態(tài),進(jìn)行中 |
fulfilled | 成功狀態(tài),操作成功完成 |
rejected | 失敗狀態(tài),操作失敗 |
const promise = new Promise((resolve, reject) => {
// 初始狀態(tài):pending
if (success) {
resolve(value); // → fulfilled
} else {
reject(error); // → rejected
}
});
三、Promise構(gòu)造函數(shù)
new Promise(executor)
executor是一個(gè)函數(shù),格式:(resolve, reject) => {}resolve(value):將 Promise 狀態(tài)變?yōu)?fulfilledreject(error):將 Promise 狀態(tài)變?yōu)?rejectedexecutor會(huì)立即執(zhí)行
const p = new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5 ? resolve('成功') : reject('失敗');
}, 1000);
});
resolve不同值的區(qū)別
- 普通值或者對(duì)象,那么這個(gè)值會(huì)作為
then回調(diào)的參數(shù) - 另外一個(gè)
Promise對(duì)象,那么這個(gè)新Promise會(huì)決定原來(lái)Promise的狀態(tài) thenable對(duì)象:這對(duì)象中有實(shí)現(xiàn)then方法,那么就會(huì)執(zhí)行該then方法,并且根據(jù)then方法的結(jié)果來(lái)決定Promise的狀態(tài)
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 3000);
});
const promise = new Promise((resolve, reject) => {
// 1. 普通值
// resolve(1);
// resolve([
// {
// name: "風(fēng)茫",
// age: 18,
// },
// {
// name: "fengmang",
// age: 30,
// },
// ]);
// 2. resolve(promise)
// 如果resolve的值為Promise對(duì)象,那么當(dāng)前的Promise的狀態(tài)由傳入的promise來(lái)決定
// resolve(p);
// 3. resolve(thenable對(duì)象)
// thenable對(duì)象: 對(duì)象有then方法,那么就會(huì)執(zhí)行then方法,并且根據(jù)then方法的結(jié)果來(lái)決定Promise的狀態(tài)
resolve({
name: "風(fēng)茫",
then: function (resolve, reject) {
Math.random() > 0.5 ? resolve(100) : reject("失敗");
},
});
});
promise
.then((res) => {
console.log("res :>> ", res);
})
.catch((err) => {
console.log(err);
});四、Promise的核心方法
1..then(onFulfilled, onRejected)
注冊(cè)成功和失敗的回調(diào)。
p.then(
(value) => { console.log(value); }, // fulfilled
(error) => { console.error(error); } // rejected
);鏈?zhǔn)秸{(diào)用的關(guān)鍵:
.then()返回一個(gè)新的Promise
then方法返回值
const promise = new Promise((resolve, reject) => {
resolve("aaa");
});
const p2 = promise
.then((res) => {
console.log(res);
return "bbb";
})
.then((res) => {
console.log(res);
return "ccc";
});
p2.then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
});
/**
* 輸出結(jié)果:
* aaa
* bbb
* ccc
*/解釋
- Promise的
then方法是返回一個(gè)新的Promise,這個(gè)新Promise的決議是等到then方法傳入的回調(diào)函數(shù) 有返回值 時(shí)進(jìn)行決議 - 若是上一個(gè)
then方法沒有使用return返回,則下一個(gè)then方法接收到的值是undefined - 若是在
then方法中return一個(gè)新的Promise,則下一個(gè)then方法需要等到新Promise決議之后才會(huì)執(zhí)行,將新的Promise決議結(jié)果返回給下一個(gè)then方法并執(zhí)行then方法
2..catch(onRejected)
捕獲錯(cuò)誤,相當(dāng)于 .then(null, onRejected)
p.catch(err => {
console.error('出錯(cuò)了:', err);
});
推薦寫法:鏈?zhǔn)?.then().catch(),統(tǒng)一錯(cuò)誤處理
fetch('/api/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error('請(qǐng)求失敗:', err));
3..finally(onFinally)
無(wú)論成功或失敗都會(huì)執(zhí)行,常用于清理資源。
p.finally(() => {
console.log('請(qǐng)求完成,關(guān)閉 loading...');
});
不接收參數(shù),不改變結(jié)果,常用于加載狀態(tài)、資源釋放。
五、Promise靜態(tài)方法
1.Promise.resolve(value)
作用:返回一個(gè) fulfilled 的 Promise。
Promise.resolve(42).then(x => console.log(x)); // 42
等價(jià)于:
new Promise(resolve => resolve(42));
使用場(chǎng)景:
- 將值包裝為Promise:如果你有一個(gè)立即可用的值,但是你希望以 Promise 的形式來(lái)處理它(比如為了保持接口的一致性),你可以使用
Promise.resolve()來(lái)將這個(gè)值包裝成一個(gè)已解決的 Promise。
let promise = Promise.resolve(42);
promise.then(function(value) {
console.log(value); // 輸出: 42
});
- 處理未知的返回值:當(dāng)你不確定某個(gè)函數(shù)會(huì)返回一個(gè)普通值還是一個(gè) Promise,但你希望統(tǒng)一用 then 方法來(lái)處理時(shí),可以使用
Promise.resolve()來(lái)包裹該返回值。
function maybeAsync() {
if (/* some condition */) {
return new Promise(/* ... */);
} else {
return 'immediate value';
}
}
Promise.resolve(maybeAsync()).then(function(result) {
console.log(result);
});- 模擬異步行為:有時(shí)候,即使數(shù)據(jù)已經(jīng)準(zhǔn)備好了,你也可能想要通過(guò)
Promise.resolve()來(lái)“延遲”執(zhí)行某些代碼,以便于和其他異步操作保持一致的流程控制。
Promise.resolve().then(() => {
console.log('This runs after the current call stack is cleared.');
});
- Promise 鏈條的開始:在構(gòu)建一個(gè)由多個(gè)步驟組成的 Promise 鏈時(shí),如果沒有初始的 Promise 可供鏈?zhǔn)秸{(diào)用
.then()或.catch()方法,可以使用Promise.resolve()作為起點(diǎn)。
Promise.resolve()
.then(() => console.log('First step'))
.then(() => console.log('Second step'));
2.Promise.reject(reason)
返回一個(gè) rejected 的 Promise。
Promise.reject('出錯(cuò)了').catch(err => console.log(err));
3.Promise.all(iterable)
作用:接收一個(gè)可迭代對(duì)象(通常是數(shù)組),其中每個(gè)元素都是一個(gè) Promise,并發(fā)執(zhí)行多個(gè) Promise,返回一個(gè)新的Promise。全部成功才成功,任一失敗則整體失敗。
const p1 = Promise.resolve('A');
const p2 = Promise.resolve('B');
const p3 = Promise.reject('C');
Promise.all([p1, p2]).then(results => {
console.log(results); // ['A', 'B']
});
Promise.all([p1, p2, p3]).catch(err => {
console.log(err); // 'C'
});適用于“所有請(qǐng)求都必須成功”的場(chǎng)景(如批量上傳)。
4.Promise.race(iterable)
返回第一個(gè)完成的 Promise(無(wú)論是成功還是失?。?。
const slow = new Promise(r => setTimeout(() => r('慢'), 2000));
const fast = new Promise(r => setTimeout(() => r('快'), 500));
Promise.race([slow, fast]).then(result => {
console.log(result); // '快'
});適用于“超時(shí)控制”:
function timeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('超時(shí)')), ms)
);
return Promise.race([promise, timeout]);
}5.Promise.allSettled(iterable)
等待所有 Promise 結(jié)束,無(wú)論成功或失敗,返回結(jié)果數(shù)組。
Promise.allSettled([p1, p2, p3]).then(results => {
results.forEach((result, i) => {
if (result.status === 'fulfilled') {
console.log(`p${i+1} 成功:`, result.value);
} else {
console.log(`p${i+1} 失敗:`, result.reason);
}
});
});
適用于“不關(guān)心成敗,只想知道所有結(jié)果”的場(chǎng)景。
6.Promise.any(iterable)
返回第一個(gè)成功的 Promise;如果全部失敗,則拋出 AggregateError。
const fail1 = Promise.reject('錯(cuò)誤1');
const fail2 = Promise.reject('錯(cuò)誤2');
const success = Promise.resolve('成功');
Promise.any([fail1, fail2, success]).then(res => {
console.log(res); // '成功'
});適用于“只要有一個(gè)成功即可”的場(chǎng)景(如多源請(qǐng)求)。
六、Promise的執(zhí)行機(jī)制(微任務(wù))
Promise 的回調(diào)(.then、.catch)屬于 微任務(wù)(microtask),在當(dāng)前宏任務(wù)結(jié)束后立即執(zhí)行。
console.log(1);
Promise.resolve().then(() => {
console.log(2);
});
console.log(3);
// 輸出:1 → 3 → 2微任務(wù)優(yōu)先級(jí)高于
setTimeout(宏任務(wù))
setTimeout(() => console.log(1), 0); Promise.resolve().then(() => console.log(2)); console.log(3); // 輸出:3 → 2 → 1
七、錯(cuò)誤處理最佳實(shí)踐
正確方式:使用.catch()
fetch('/api')
.then(res => res.json())
.then(data => { throw new Error('處理失敗'); })
.catch(err => console.error(err)); // 能捕獲
錯(cuò)誤方式:在.then中不處理錯(cuò)誤
fetch('/api')
.then(res => res.json(), err => console.error(err)) // 只能捕獲 fetch 失敗
.then(data => { throw new Error('處理失敗'); }) // 這個(gè)錯(cuò)誤沒被捕獲!
建議:鏈?zhǔn)秸{(diào)用末尾加
.catch()
八、Promise的常見模式
1. 串行執(zhí)行(鏈?zhǔn)秸{(diào)用)
function asyncTask(name) {
return Promise.resolve().then(() => {
console.log(`執(zhí)行 ${name}`);
return name;
});
}
asyncTask('A')
.then(() => asyncTask('B'))
.then(() => asyncTask('C'));2. 并行執(zhí)行
Promise.all([
asyncTask('A'),
asyncTask('B'),
asyncTask('C')
]).then(results => console.log('全部完成'));
3. 重試機(jī)制
function retry(fn, times = 3) {
return fn().catch(err => {
if (times <= 1) throw err;
return retry(fn, times - 1);
});
}
retry(() => fetch('/api')).catch(console.error);九、Promise的局限性
| 問(wèn)題 | 說(shuō)明 |
|---|---|
| 無(wú)法取消 | 一旦創(chuàng)建,無(wú)法中途取消 |
| 錯(cuò)誤冒泡 | 未捕獲的錯(cuò)誤可能靜默失?。∟ode.js 會(huì)警告) |
| 無(wú)法獲取進(jìn)度 | 只有成功/失敗,不支持 onProgress |
冗長(zhǎng)的 .then 鏈 | 復(fù)雜邏輯仍難讀 |
解決方案:使用
async/await(ES2017)
async function getData() {
try {
const res = await fetch('/api');
const data = await res.json();
return data;
} catch (err) {
console.error('請(qǐng)求失敗:', err);
}
}
十、總結(jié)
| 項(xiàng)目 | 說(shuō)明 |
|---|---|
| 定位 | 異步編程的標(biāo)準(zhǔn)化解決方案 |
| 核心價(jià)值 | 解決回調(diào)地獄,提供鏈?zhǔn)秸{(diào)用和統(tǒng)一錯(cuò)誤處理 |
| 關(guān)鍵方法 | .then()、.catch()、.finally() |
| 靜態(tài)方法 | all、race、allSettled、any、resolve、reject |
| 執(zhí)行機(jī)制 | 回調(diào)屬于微任務(wù),優(yōu)先級(jí)高 |
| 現(xiàn)代替代 | async/await(基于 Promise) |
最終結(jié)論:Promise 是現(xiàn)代 JavaScript 異步編程的基石。即使你使用 async/await,其底層仍是 Promise。掌握 Promise 是成為合格前端/Node.js 開發(fā)者的必經(jīng)之路。
到此這篇關(guān)于JavaScript Promise錯(cuò)誤處理與最佳實(shí)踐的文章就介紹到這了,更多相關(guān)js promise內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
動(dòng)態(tài)創(chuàng)建樣式表在各瀏覽器中的差異測(cè)試代碼
對(duì)于標(biāo)準(zhǔn)瀏覽器,直接使用css.innerHTML也可以修改HTMLStyleElement的css規(guī)則2011-09-09
js實(shí)現(xiàn)移動(dòng)端簡(jiǎn)易滑動(dòng)表格
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)移動(dòng)端簡(jiǎn)易滑動(dòng)表格,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
uniapp實(shí)現(xiàn)下拉刷新與上拉觸底加載功能的示例代碼
這篇文章主要記錄一下uniapp實(shí)現(xiàn)下拉刷新與上拉觸底加載功能的示例代碼,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-04-04
使用typescript+webpack構(gòu)建一個(gè)js庫(kù)的示例詳解
這篇文章主要介紹了typescript+webpack構(gòu)建一個(gè)js庫(kù),本文主要記錄使用typescript配合webpack打包一個(gè)javascript library的配置過(guò)程,需要的朋友可以參考下2022-07-07
JavaScript中Cookie的簡(jiǎn)介和使用方法詳解
cookie是瀏覽器提供的一種機(jī)制,它將document對(duì)象的cookie屬性提供給JavaScript,這篇文章主要給大家介紹了關(guān)于JavaScript中Cookie的簡(jiǎn)介和使用方法,需要的朋友可以參考下2025-05-05
BootStrap 智能表單實(shí)戰(zhàn)系列(二)BootStrap支持的類型簡(jiǎn)介
這篇文章主要介紹了BootStrap 智能表單實(shí)戰(zhàn)系列(二)BootStrap支持的類型簡(jiǎn)介 的相關(guān)資料,非常不錯(cuò)具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧2016-06-06
javascript設(shè)計(jì)模式 – 狀態(tài)模式原理與用法實(shí)例分析
這篇文章主要介紹了javascript設(shè)計(jì)模式 – 狀態(tài)模式,結(jié)合實(shí)例形式分析了javascript狀態(tài)模式相關(guān)概念、原理、用法及操作注意事項(xiàng),需要的朋友可以參考下2020-04-04

