前端開發使用 Promise 時容易犯的5個錯誤
Promise 提供了一種優雅的方法來處理 JS 中的異步操作。這也是避免“回調地獄”的解決方案。然而,并沒有多少開發人員了解其中的內容。因此,許多人在實踐中往往會犯錯誤。
在本文中,介紹一下使用 promise 時的五個常見錯誤,希望大家能夠避免這些錯誤。
1.避免 Promise 地獄
通常,Promise是用來避免回調地獄。但濫用它們也會導致 Promise是地獄。
userLogin('user').then(function(user){ getArticle(user).then(function(articles){ showArticle(articles).then(function(){ //Your code goes here... }); }); });
在上面的例子中,我們對?userLogin
、getararticle
?和?showararticle
?嵌套了三個promise。這樣復雜性將按代碼行比例增長,它可能變得不可讀。
為了避免這種情況,我們需要解除代碼的嵌套,從第一個?then
?中返回?getArticle
,然后在第二個?then
?中處理它。
userLogin('user') .then(getArticle) .then(showArticle) .then(function(){ //Your code goes here... });
2. 在 Promise 中使用?try…catch?塊
通常情況下,我們使用?try...catch
?塊來處理錯誤。然而,不建議在?Promise
?對象中使用try...catch
?。
這是因為如果有任何錯誤,Promise對象會在?catch
?內自動處理。
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));
在上面的例子中,我們在Promise 內使用了?try...catch
?塊。
但是,Promise本身會在其作用域內捕捉所有的錯誤(甚至是打字錯誤),而不需要?try...catch
塊。它確保在執行過程中拋出的所有異常都被獲取并轉換為被拒絕的 Promise。
new Promise((resolve, reject) => { const data = doThis(); // do something resolve() }) .then(data => console.log(data)) .catch(error => console.log(error));
注意:在 Promise 塊中使用?.catch()
?塊是至關重要的。否則,你的測試案例可能會失敗,而且應用程序在生產階段可能會崩潰。
3. 在 Promise 塊內使用異步函數
Async/Await
?是一種更高級的語法,用于處理同步代碼中的多個Promise。當我們在一個函數聲明前使用?async
?關鍵字時,它會返回一個 Promise,我們可以使用?await
?關鍵字來停止代碼,直到我們正在等待的Promise解決或拒絕。
但是,當你把一個 Async 函數放在一個 Promise 塊里面時,會有一些副作用。
假設我們想在Promise 塊中做一個異步操作,所以使用了?async
?關鍵字,但,不巧的是我們的代碼拋出了一個錯誤。
這樣,即使使用?catch()
?塊或在?try...catch
?塊內等待你的Promise,我們也不能立即處理這個錯誤。請看下面的例子。
// 此代碼無法處理錯誤 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); } })();
當我在Promise塊內遇到?async
?函數時,我試圖將?async
?邏輯保持在 Promise 塊之外,以保持其同步性。10次中有9次都能成功。
然而,在某些情況下,可能需要一個?async
?函數。在這種情況下,也別無選擇,只能用try/catch
?塊來手動管理。
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.在創建 Promise 后立即執行 Promise 塊
至于下面的代碼片斷,如果我們把代碼片斷放在調用HTTP請求的地方,它就會被立即執行。
const myPromise = new Promise(resolve => { // code to make HTTP request resolve(result); });
原因是這段代碼被包裹在一個Promise構造函數中。然而,有些人可能會認為只有在執行myPromise
?的then
方法之后才被觸發。
然而,真相并非如此。相反,當一個Promise被創建時,回調被立即執行。
這意味著在建立?myPromise
?之后到達下面一行時,HTTP請求很可能已經在運行,或者至少處于調度狀態。
Promises 總是急于執行過程。
但是,如果希望以后再執行 Promises,應該怎么做?如果現在不想發出HTTP請求怎么辦?是否有什么神奇的機制內置于 Promises 中,使我們能夠做到這一點?
答案就是使用函數。函數是一種耗時的機制。只有當開發者明確地用?()
?來調用它們時,它們才會執行。簡單地定義一個函數還不能讓我們得到什么。所以,讓 Promise 變得懶惰的最有效方法是將其包裹在一個函數中!
const createMyPromise = () => new Promise(resolve => { // HTTP request resolve(result); });
對于HTTP請求,Promise 構造函數和回調函數只有在函數被執行時才會被調用。所以現在我們有一個懶惰的Promise,只有在我們需要的時候才會執行。
5. 不一定使用 Promise.all() 方法
如果你已經工作多年,應該已經知道我在說什么了。如果有許多彼此不相關的 Promise,我們可以同時處理它們。
Promise 是并發的,但如你一個一個地等待它們,會太費時間,Promise.all()
可以節省很多時間。
記住,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'); })();
上述代碼的執行時間約為?6?秒。但如果我們用?Promise.all()
?代替它,將減少執行時間。
(async () => { console.time('concurrent'); await Promise.all([f1(), f2(), f3()]); console.timeEnd('concurrent'); })();
結語
以上就是本文關于前端開發使用 Promise 時容易犯的5個錯誤總結,當然,開發中可能還會有很多簡單的問題需要仔細解決,如果大家有更多相關錯誤,歡迎留言討論。
1. 本站所有文章教程及資源素材均來源于網絡與用戶分享或為本站原創,僅限用于學習和研究。
2. 如果內容損害你的權益請聯系客服QQ:1642748312給予處理。
碼云筆記 » 前端開發使用 Promise 時容易犯的5個錯誤