天天看點

async function_在 async/await 中更好的處理錯誤

async function_在 async/await 中更好的處理錯誤
async function_在 async/await 中更好的處理錯誤

本篇文章介紹在使用

async

/

await

文法時,一種更好的處理錯誤的方式。在此之前,大家也需要先了解下 Promise 的工作原理。

從回調地獄到 Promise

回調地獄(callback Hell),也稱為“末日金字塔(Pyramid of Doom)”,是在開發者代碼中看到的一種反模式(anti-pattern),這種異步程式設計方式并不明智。- Colin Toh

由于回調函數的嵌套,回調地獄 會使你的代碼向右排布而不是垂直向下排版。

為了更直覺的反映回調函數,這裡舉了一個例子。

使用者資料案例 1

// Code that reads from left to right// instead of top to bottomlet user;let friendsOfUser;getUser(userId, function(data) {  user = data;  getFriendsOfUser(userId, function(friends) {    friendsOfUser = friends;    getUsersPosts(userId, function(posts) {      showUserProfilePage(user, friendsOfUser, posts, function() {        // Do something here      });    });  });});
           

Promise

Promise 是 ES2015(即俗稱的 ES6)引入的一個語言特性,用來更好的處理異步操作,避免回調地獄的出現。

下例中使用 Promise 的

.then

鍊來解決回調地獄問題。

使用者資料案例 2

// A solution with promiseslet user;let friendsOfUser;getUser().then(data => {  user = data;  return getFriendsOfUser(userId);}).then(friends => {  friendsOfUser = friends;  return getUsersPosts(userId);}).then(posts => {  showUserProfilePage(user, friendsOfUser, posts);}).catch(e => console.log(e));
           

Promise 的處理方式更加幹淨和可讀。

async/await Promise

async

/

await

是一種特殊的文法,可以用更簡潔的方式處理 Promise。在

funtion

前加

async

關鍵字就能将函數轉換成 Promise。

所有的 async 函數的傳回值都是 Promise。

例子

// Arithmetic addition functionasync function add(a, b) {  return a + b;}// Usage:add(1, 3).then(result => console.log(result));// Prints: 4
           

使用

async

/

await

,可以讓“使用者資料案例 2”看起來更棒。

使用者資料案例 3

async function userProfile() {  let user = await getUser();  let friendsOfUser = await getFriendsOfUser(userId);  let posts = await getUsersPosts(userId);  showUserProfilePage(user, friendsOfUser, posts);}
           

等等!有個問題

在“使用者資料案例 3”中,如果有一個 Promise reject 了,就會抛出 

Unhandled promise rejection

異常。

在此之前寫的代碼都沒有考慮 Promise reject 的情況。未處理的 reject Promise 過去會以靜默的方式失敗,這可能會使調試成為噩夢。

不過現在,Promise reject 時會抛出一個錯誤了。

  • Google Chrome 抛出的錯誤:

    VM664:1 Uncaught (in promise) Error

  • Node 抛出的錯誤則類似這樣:

    (node:4796) UnhandledPromiseRejectionWarning: Unhandled promise rejection (r ejection id: 1): Error: spawn cmd ENOENT

[1] (node:4796) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code

No promise should be left uncaught(每個 Promise 都要使用

.catch

處理). - Javascript

注意,“使用者資料案例 2”中的 

.catch

方法。如果沒有寫 

.catch

塊的話,JavaScript 會在 Promise reject 的時候抛出 

Unhandled promise rejection

錯誤。

處理“使用者資料案例 3”中的問題比較容易。隻要使用

try...catch

塊包裝下

await

語句就能避免 

Unhandled promise rejection

錯誤了。

使用者資料案例 4

async function userProfile() {  try {    let user = await getUser();    let friendsOfUser = await getFriendsOfUser(userId);    let posts = await getUsersPosts(userId);    showUserProfilePage(user, friendsOfUser, posts);  } catch(e) {    console.log(e);  }}
           

問題解決了!

但是錯誤處理還可以再優雅點嗎?

我怎麼知道報錯是來自哪一個異步請求的呢?可以在異步請求上使用

.catch

方法來處理錯誤。

使用者資料案例 5

let user = await getUser().catch(e => console.log('Error: ', e.message));let friendsOfUser = await getFriendsOfUser(userId).catch(e => console.log('Error: ', e.message));let posts = await getUsersPosts(userId).catch(e => console.log('Error: ', e.message));showUserProfilePage(user, friendsOfUser, posts);
           

上面的解決方案将處理來自請求的單個錯誤,但是會混合使用多種模式。應該有一種更幹淨的方法來使用

async

/

await

而不使用

.catch

方法(嗯,如果你不介意的話,可以這樣做)。

我的更好的 async/await 錯誤處理方案

使用者資料案例 6

/** * @description ### Returns Go / Lua like responses(data, err) * when used with await * * - Example response [ data, undefined ] * - Example response [ undefined, Error ] * * * When used with Promise.all([req1, req2, req3]) * - Example response [ [data1, data2, data3], undefined ] * - Example response [ undefined, Error ] * * * When used with Promise.race([req1, req2, req3]) * - Example response [ data, undefined ] * - Example response [ undefined, Error ] * * @param {Promise} promise * @returns {Promise} [ data, undefined ] * @returns {Promise} [ undefined, Error ] */const handle = (promise) => {  return promise    .then(data => ([data, undefined]))    .catch(error => Promise.resolve([undefined, error]));}async function userProfile() {  let [user, userErr] = await handle(getUser());  if(userErr) throw new Error('Could not fetch user details');  let [friendsOfUser, friendErr] = await handle(    getFriendsOfUser(userId)  );  if(friendErr) throw new Error('Could not fetch user\'s friends');  let [posts, postErr] = await handle(getUsersPosts(userId));  if(postErr) throw new Error('Could not fetch user\'s posts');  showUserProfilePage(user, friendsOfUser, posts);}
           

這裡使用了一個工具函數 

handle

,如此就可以避免 

Unhandled promise rejection

報錯,還能細粒度的處理錯誤。

解釋

handle

函數接受一個 Promise 對象作為參數,并總是 resolve 它,以

[data|undefined, Error|undefined]

的形式傳回結果。

  • 如果 Promise resolve 了,

    handle

    函數傳回

    [data, undefined]

  • 如果 Promise reject 了,

    handle

    函數傳回

    [undefined, Error]

結論

async

/

await

的文法很簡潔,但你還是要處理異步函數裡的抛出的錯誤。除非你實作了自定義錯誤類(custom error classes),否則很難處理

Promise.then

鍊中的 

.catch

錯誤處理。使用

handle

工具函數,我們可以避免

Unhandled promise rejection

報錯,還能細粒度的處理錯誤。

(轉載)

作者:zhangbao90s

原文連結:https://juejin.im/post/5e535624518825496e784ccb

async function_在 async/await 中更好的處理錯誤

END

async function_在 async/await 中更好的處理錯誤
async function_在 async/await 中更好的處理錯誤

  點選