JavaScript Promise 高級技巧:避免常見陷阱與解決方案
我曾在前文中介紹過 JavaScript Promise 的基礎(chǔ)知識 以及 如何使用 async/await 關(guān)鍵字 來簡化異步代碼。本文將深入探討 JavaScript Promise 的高級應(yīng)用,分析開發(fā)者常遇到的四個陷阱及其解決方案。
陷阱一:Promise 處理器總是返回 Promise
無論是 then 還是 catch 處理器,只要返回值,該值就會被自動包裝成 Promise(如果它本身不是 Promise)。因此,永遠(yuǎn)不需要這樣寫代碼:
firstAjaxCall.then(() => {
return new Promise((resolve, reject) => {
nextAjaxCall().then(() => resolve());
});
});
由于 nextAjaxCall 本身返回 Promise,可以簡化為:
firstAjaxCall.then(() => {
return nextAjaxCall();
});
對于普通值(非 Promise),處理器會將其包裝為已解析的 Promise,因此可以繼續(xù)鏈?zhǔn)秸{(diào)用:
firstAjaxCall.then((response) => {
return response.importantField
}).then((resolvedValue) => {
// resolvedValue 就是上面返回的 response.importantField
console.log(resolvedValue);
});
解決方案一:使用 Promise.resolve() 處理不確定值
當(dāng)不確定輸入值是否為 Promise 時,可以使用靜態(tài)方法 Promise.resolve()。該方法會自動處理兩種情況:
let processInput = (maybePromise) => {
let definitelyPromise = Promise.resolve(maybePromise);
definitelyPromise.then(doSomeWork);
};
陷阱二:.then() 只接受函數(shù)參數(shù)
常見錯誤寫法:
let getAllArticles = () => someAjax.get('/articles');
let getArticleById = (id) => someAjax.get(`/articles/${id}`);
getAllArticles().then(getArticleById(2));
本意是先獲取所有文章,再獲取 ID 為 2 的文章,但實際上兩個請求會同時發(fā)起。問題在于 JavaScript 會立即執(zhí)行 getArticleById(2),而不是將其作為函數(shù)傳遞。
解決方案一:使用箭頭函數(shù)包裝
getAllArticles().then(() => getArticleById(2));
解決方案二:傳遞命名函數(shù)引用
let getArticle2 = () => getArticleById(2);
getAllArticles().then(getArticle2);
解決方案三:使用 async/await
async function getSequentially() {
const allArticles = await getAllArticles();
const specificArticle = await getArticleById(2);
// 使用 specificArticle
}
陷阱三:非函數(shù)參數(shù)導(dǎo)致的意外行為
錯誤示例:
getAllArticles().then(getArticleById(2)).then((article2) => {
// article2 實際上是 getAllArticles() 的解析值
});
由于第一個 .then() 接收的是立即執(zhí)行結(jié)果而非函數(shù),會導(dǎo)致后續(xù)處理器獲取到錯誤的值。
解決方案一:使用帶形參的命名函數(shù)
let extractId = (article) => article.id;
getFirstArticle().then(extractId).then(getCommentsForArticleId);
解決方案二:使用 async/await
async function getArticleAndComments() {
const article = await getFirstArticle();
const comments = await getCommentsForArticleId(article.id);
// 使用 comments
}
陷阱四:async/await 破壞并發(fā)性
錯誤示例(順序執(zhí)行):
async function getMultipleUsersSequentially(userIds) {
const users = [];
for (const id of userIds) {
const user = await fetchUserDataPromise(id); // 每次等待
users.push(user);
}
return users;
}
// 三個請求需要約 4.5 秒(每個 1.5 秒)
解決方案:使用 Promise.all 實現(xiàn)并發(fā)
async function getMultipleUsersConcurrently(userIds) {
const promises = userIds.map(id => fetchUserDataPromise(id));
const users = await Promise.all(promises);
return users;
}
// 三個并發(fā)請求只需約 1.5 秒
總結(jié)
雖然 async/await 能解決許多 Promise 問題,但深入理解 Promise 機(jī)制至關(guān)重要。掌握這些陷阱及其解決方案,將幫助您編寫更健壯的異步代碼。
原文地址:https://www.infoworld.com/article/3999603/javascript-promises-4-gotchas-and-how-to-avoid-them.html