如何優雅的處理Promise錯誤

關注公衆號前端小白菜,更多前端小乾貨等着你喔!公衆號會不按期分享前端技術,天天進步一點點,與你們相伴成長前端

Promise的出現,成功的將廣大前端開發者從回調地域的深淵中拯救了出來,然後還有Async/Await,這二者使得開發者更容易編寫異步代碼,而後當處理錯誤時,貌似就不是那麼的美好了。promise

先模擬一個請求操做:異步

const fetchData = (url, shouldReject = false) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      shouldReject ? reject('error') : resolve('this is data');
    }, 1000);
  });
};

簡單回顧一下Promise與Async/Await,例如發起三個請求,使用Promise能夠async

fetchData('url1')
  .then(data => {
    console.log('data1:', data);
    return fetchData('url2');
  })
  .then(data => {
    console.log('data2:', data);
    return fetchData('url3');
  })
  .then(data => {
    console.log('data3:', data);
  })
  .catch(error => console.log('error:', error));

看起來還不錯,這也是Promise比之回調函數的優點在所在,代碼看起來很優雅了,可是Async/Await能夠更加優雅。函數

(async () => {
  const data1 = await fetchData('url1');
  console.log('data1:', data1);
​
​
  const data2 = await fetchData('url2');
  console.log('data2:', data2);
​
​
  const data3 = await fetchData('url3');
  console.log('data3:', data3);
})();

代碼看起來很優美,甚至Async/Await更加像同步代碼,也更符合人的正常邏輯了,可是再看下Promise的例子,全部的異常都在最後處理,那麼出現異常的時候,這個異常是哪一個請求的呢?難道還要給錯誤定義類型不成?fetch

固然Promise也能夠逐一捕獲異常,this

fetchData('url1')
  .catch(error => console.log('error1:', error))
  .then(data => {
    console.log('data1:', data);
    return fetchData('url2', true);
  })
  .catch(error => console.log('error2:', error))
  .then(data => {
    console.log('data2:', data);
    return fetchData('url3');
  })
  .catch(error => console.log('error3:', error))
  .then(data => {
    console.log('data3:', data);
  });
// output
// data1:this is data
// error2:error
// data2:undefined
// data3:this is data

能夠看到,雖然能夠捕獲每個異常了,可是也因爲每一層都作了異常處理,致使Promise並不會由於出現reject而終止執行,寫起來也很麻煩,這不是咱們想要的。url

Async/Await是Promise的語法糖,咱們可使用try-catch來捕獲異常,也可使用Promise的catch來捕獲。spa

(async () => {
  try {
    const data1 = await fetchData('url1', true);
    console.log('data1:', data1);
  } catch (error) {
    console.log('error1:', error);
  }
​
  const data2 = await fetchData('url2', true).catch(error => console.log('error2:', error));
  console.log('data2:', data2);
​
  const data3 = await fetchData('url3').catch(error => console.log('error3:', error));
  console.log('data3:', data3);
})();
​
// output
// error1: error
// error2:error
// data2:undefined
// data3:this is data

若是使用Promise的catch來捕獲異常,而且對返回值作處理,能夠很好的實現異常處理,而且出錯阻止往下執行。code

(async () => {
  const data1 = await fetchData('url1', true).catch(error => console.log('error1:', error));
  if (!data1) return;
  console.log('data1:', data1);
​
  const data2 = await fetchData('url2', true).catch(error => console.log('error2:', error));
  if (!data2) return;
  console.log('data2:', data2);
​
  const data3 = await fetchData('url3').catch(error => console.log('error3:', error));
  if (!data3) return;
  console.log('data3:', data3);
})();
// output
// error1: error

那麼有沒有更優雅一點的寫法呢?固然是有的,若是對使用Promise的業務函數作一點處理,結合Promise與Async/Await,就能夠更加優雅的處理異步業務了。

// 普通未處理的異步函數
const fetchDataPromise = (url, shouldReject = false) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      shouldReject ? reject('error') : resolve('this is data');
    }, 1000);
  });
};
​
// 普通異步函數處理
const fetchData = (url, shouldReject = false) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      shouldReject ? resolve(['error', null]) : resolve([null, 'this is data']);
    }, 1000);
  });
};
// 對異步函數處理
const asyncFunc = (url, shouldReject = false) => {
  return fetchDataPromise(url, shouldReject)
    .then(data => [null, data])
    .catch(error => [error, null])
}
​
// 抽離公共函數處理異步函數
const promiseWrap = (promise) => {
  return promise
    .then(data => [null, data])
    .catch(error => [error, null])
}

有了如上的改造後,業務代碼就能夠這樣寫了。

(async () => {
  const [error1, data1] = await fetchData('url1', true);
  if (error1) {
    console.log('error1:', error1);
    // do more thing
  }
  console.log('data1:', data1);
​
  const [error2, data2] = await asyncFunc('url2', true);
  if (error2) {
    console.log('error2:', error2);
  }
  console.log('data2:', data2);
​
  const [error3, data3] = await promiseWrap(fetchDataPromise('url3', true));
  if (error3) {
    console.log('error3:', error3);
  }
})();
// output
// error1: error
// data1: null
// error2: error
// data2: null
// error3: error
// data3: null

這樣寫就優雅多了,也更加容易處理異常,其實這種處理方法在GO語言中很是常見。

本文首發於本人公衆號,前端小白菜,分享與關注前端技術,歡迎關注~~

前端小白菜

相關文章
相關標籤/搜索