JS使用Promise時(shí)常見的5個(gè)錯(cuò)誤總結(jié)
Promise 提供了一種優(yōu)雅的方法來處理 JS 中的異步操作。這也是避免“回調(diào)地獄”的解決方案。然而,并沒有多少開發(fā)人員了解其中的內(nèi)容。因此,許多人在實(shí)踐中往往會(huì)犯錯(cuò)誤。
在本文中,介紹一下使用 promise 時(shí)的五個(gè)常見錯(cuò)誤,希望大家能夠避免這些錯(cuò)誤。
1.避免 Promise 地獄
通常,Promise是用來避免回調(diào)地獄。但濫用它們也會(huì)導(dǎo)致 Promise是地獄。
userLogin('user').then(function(user){ getArticle(user).then(function(articles){ showArticle(articles).then(function(){ //Your code goes here... }); }); });
在上面的例子中,我們對(duì) userLogin
、getararticle
和 showararticle
嵌套了三個(gè)promise。這樣復(fù)雜性將按代碼行比例增長,它可能變得不可讀。
為了避免這種情況,我們需要解除代碼的嵌套,從第一個(gè) then
中返回 getArticle
,然后在第二個(gè) then
中處理它。
userLogin('user') .then(getArticle) .then(showArticle) .then(function(){ //Your code goes here... });
2.在 Promise 中使用 try/catch 塊
通常情況下,我們使用 try/catch
塊來處理錯(cuò)誤。然而,不建議在 Promise
對(duì)象中使用try/catch
。
這是因?yàn)槿绻腥魏五e(cuò)誤,Promise對(duì)象會(huì)在 catch
內(nèi)自動(dòng)處理。
ew Promise((resolve, reject) => { try { const data = doThis(); // do something resolve(); } catch (e) { reject(e); } }) .then(data => console.log(data)) .catch(error => console.log(error));
在上面的例子中,我們?cè)赑romise 內(nèi)使用了 try/catch
塊。
但是,Promise本身會(huì)在其作用域內(nèi)捕捉所有的錯(cuò)誤(甚至是打字錯(cuò)誤),而不需要 try/catch
塊。它確保在執(zhí)行過程中拋出的所有異常都被獲取并轉(zhuǎn)換為被拒絕的 Promise。
new Promise((resolve, reject) => { const data = doThis(); // do something resolve() }) .then(data => console.log(data)) .catch(error => console.log(error));
注意:在 Promise 塊中使用 .catch()
塊是至關(guān)重要的。否則,你的測試案例可能會(huì)失敗,而且應(yīng)用程序在生產(chǎn)階段可能會(huì)崩潰。
3.在 Promise 塊內(nèi)使用異步函數(shù)
Async/Await
是一種更高級(jí)的語法,用于處理同步代碼中的多個(gè)Promise。當(dāng)我們?cè)谝粋€(gè)函數(shù)聲明前使用 async
關(guān)鍵字時(shí),它會(huì)返回一個(gè) Promise,我們可以使用 await
關(guān)鍵字來停止代碼,直到我們正在等待的Promise解決或拒絕。
但是,當(dāng)你把一個(gè) Async 函數(shù)放在一個(gè) Promise 塊里面時(shí),會(huì)有一些副作用。
假設(shè)我們想在Promise 塊中做一個(gè)異步操作,所以使用了 async
關(guān)鍵字,但,不巧的是我們的代碼拋出了一個(gè)錯(cuò)誤。
這樣,即使使用 catch()
塊或在 try/catch
塊內(nèi)等待你的Promise,我們也不能立即處理這個(gè)錯(cuò)誤。請(qǐng)看下面的例子。
// 此代碼無法處理錯(cuò)誤 new Promise(async () => { throw new Error('message'); }).catch(e => console.log(e.message)); (async () => { try { await new Promise(async () => { throw new Error('message'); }); } catch (e) { console.log(e.message); } })();
當(dāng)我在Promise塊內(nèi)遇到 async
函數(shù)時(shí),我試圖將 async
邏輯保持在 Promise 塊之外,以保持其同步性。10次中有9次都能成功。
然而,在某些情況下,可能需要一個(gè) async
函數(shù)。在這種情況下,也別無選擇,只能用try/catch
塊來手動(dòng)管理。
new Promise(async (resolve, reject) => { try { throw new Error('message'); } catch (error) { reject(error); } }).catch(e => console.log(e.message)); //using async/await (async () => { try { await new Promise(async (resolve, reject) => { try { throw new Error('message'); } catch (error) { reject(error); } }); } catch (e) { console.log(e.message); } })();
4.在創(chuàng)建 Promise 后立即執(zhí)行 Promise 塊
至于下面的代碼片斷,如果我們把代碼片斷放在調(diào)用HTTP請(qǐng)求的地方,它就會(huì)被立即執(zhí)行。
const myPromise = new Promise(resolve => { // code to make HTTP request resolve(result); });
原因是這段代碼被包裹在一個(gè)Promise構(gòu)造函數(shù)中。然而,有些人可能會(huì)認(rèn)為只有在執(zhí)行myPromise
的then
方法之后才被觸發(fā)。
然而,真相并非如此。相反,當(dāng)一個(gè)Promise被創(chuàng)建時(shí),回調(diào)被立即執(zhí)行。
這意味著在建立 myPromise
之后到達(dá)下面一行時(shí),HTTP請(qǐng)求很可能已經(jīng)在運(yùn)行,或者至少處于調(diào)度狀態(tài)。
Promises 總是急于執(zhí)行過程。
但是,如果希望以后再執(zhí)行 Promises,應(yīng)該怎么做?如果現(xiàn)在不想發(fā)出HTTP請(qǐng)求怎么辦?是否有什么神奇的機(jī)制內(nèi)置于 Promises 中,使我們能夠做到這一點(diǎn)?
答案就是使用函數(shù)。函數(shù)是一種耗時(shí)的機(jī)制。只有當(dāng)開發(fā)者明確地用 ()
來調(diào)用它們時(shí),它們才會(huì)執(zhí)行。簡單地定義一個(gè)函數(shù)還不能讓我們得到什么。所以,讓 Promise 變得懶惰的最有效方法是將其包裹在一個(gè)函數(shù)中!
const createMyPromise = () => new Promise(resolve => { // HTTP request resolve(result); });
對(duì)于HTTP請(qǐng)求,Promise 構(gòu)造函數(shù)和回調(diào)函數(shù)只有在函數(shù)被執(zhí)行時(shí)才會(huì)被調(diào)用。所以現(xiàn)在我們有一個(gè)懶惰的Promise,只有在我們需要的時(shí)候才會(huì)執(zhí)行。
5.不一定使用 Promise.all() 方法
如果你已經(jīng)工作多年,應(yīng)該已經(jīng)知道我在說什么了。如果有許多彼此不相關(guān)的 Promise,我們可以同時(shí)處理它們。
Promise 是并發(fā)的,但如你一個(gè)一個(gè)地等待它們,會(huì)太費(fèi)時(shí)間,Promise.all()
可以節(jié)省很多時(shí)間。
記住,Promise.all() 是我們的朋友
const { promisify } = require('util'); const sleep = promisify(setTimeout); async function f1() { await sleep(1000); } async function f2() { await sleep(2000); } async function f3() { await sleep(3000); } (async () => { console.time('sequential'); await f1(); await f2(); await f3(); console.timeEnd('sequential'); })();
上述代碼的執(zhí)行時(shí)間約為 6
秒。但如果我們用 Promise.all()
代替它,將減少執(zhí)行時(shí)間。
(async () => { console.time('concurrent'); await Promise.all([f1(), f2(), f3()]); console.timeEnd('concurrent'); })();
總結(jié)
在這篇文章中,我們討論了使用 Promise 時(shí)常犯的五個(gè)錯(cuò)誤。然而,可能還有很多簡單的問題需要仔細(xì)解決。
到此這篇關(guān)于JS使用Promise時(shí)常見的5個(gè)錯(cuò)誤總結(jié)的文章就介紹到這了,更多相關(guān)JS Promise錯(cuò)誤內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript中執(zhí)行上下文和執(zhí)行棧
這篇文章主要介紹了JavaScript中執(zhí)行上下文和執(zhí)行棧,執(zhí)行上下文是評(píng)估和執(zhí)行JavaScript代碼的環(huán)境的抽象概念,更多相關(guān)介紹,感興趣的朋友可以參考一下2022-09-09JavaScript實(shí)現(xiàn)漢字轉(zhuǎn)換為拼音的庫文件示例
這篇文章主要介紹了JavaScript實(shí)現(xiàn)漢字轉(zhuǎn)換為拼音的庫文件,結(jié)合具體實(shí)例分析了JSPinyin庫文件與簡單使用技巧,需要的朋友可以參考下2016-12-12JavaScript async/await原理及實(shí)例解析
這篇文章主要介紹了JavaScript async/await原理及實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-1210個(gè)很少使用的JavaScript?Console方法分享
你一定聽說過?console.log()?,而且可能一直在使用它,在本文中,我們將探討一些最有用的控制臺(tái)方法,以及它們?cè)跀?shù)據(jù)可視化、調(diào)試等方面的用途,感興趣的小伙伴可以學(xué)習(xí)一下2023-09-09JS通過ajax動(dòng)態(tài)讀取xml文件內(nèi)容的方法
這篇文章主要介紹了JS通過ajax動(dòng)態(tài)讀取xml文件內(nèi)容的方法,實(shí)例分析了Ajax操作XML文件的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03詳解JSONObject和JSONArray區(qū)別及基本用法
這篇文章主要介紹了詳解JSONObject和JSONArray區(qū)別及基本用法,需要的朋友可以參考下2017-10-10Javascript將圖片的絕對(duì)路徑轉(zhuǎn)換為base64編碼的方法
這篇文章主要介紹了Javascript將圖片的絕對(duì)路徑轉(zhuǎn)換為base64編碼的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01