async, await and try catch

栏目: JavaScript · 发布时间: 5年前

内容简介:這篇想說一下就等同於:

async, await and try catch

這篇想說一下 asyncawait 語法的一些小細節,首先從 async 來說吧,一般來說,async function 是在內部有需要用 await 等 Promise 結果的時候才使用,也由於這個特性,async function 的回傳值都會是個 Promise,意思就是你回傳非 Promise 的值,會 自動被包成 Promise ,所以像下面的程式:

async function wow () {
    return Promise.resolve(100);
}

now().then(v => { console.log(v); });

就等同於:

async function wow () {
    return 100;
}

now().then(v => { console.log(v); });

和直接回 Promise value 比起來,效能上不會有什麼顯著差異,從建議的實做方法來看就是多一個判斷。再來看看 await 吧,首先一樣, await 一般是用來接 Promise 的,不過其實也是可以接非 Promise value 的

async function wow () {
  var r = await 1;
  console.log(1);
}

wow();
console.log(2);

所以這樣的程式碼也可以正確執行,不過 await 那邊的執行方式還是會維持非同步的(實際上應該是後面的東西都會用 Promise 包起來一次),所以這段程式碼的輸出會是先輸出 2 再輸出 1

再來這點可能比較多人知道,就是連續的多個 await 不會讓這些非同步操作同時開始:

async function wow () {
  const a = await fetch('/a');
  const b = await fetch('/b');
  const c = await fetch('/c');

  return [a, b, c];
}

這樣其實三個請求會照順序執行, a 有結果了才去要 bb 有結果了才去要 c ,而不是同時處理,如果要同時發出請求則還是需要用 Promise.all ,然後不用 async 了:

function wow () {
  return Promise.all([
    fetch('/a'),
    fetch('/b'),
    fetch('/c')
  ]);
}

不要 await 的話,也是可以先 assign 給變數的:

function wow () {
  const a = fetch('/a');
  const b = fetch('/b');
  const c = fetch('/c');

  return Promise.all([a, b, c]);
}

然後其實 Promise.all 是要所有的 Promise 都 fulfilled 時才會 resolve,另外一個角度來看,就是其中只要一個 rejected 的話,就不會 resolve,實際上使用起來變化有點少,而且要做忽略錯誤的 fetch 也有點麻煩,所以現在 TC39 還有個新的草案叫 Promise.allSettled ,不管是 resolve 還是 reject,只要所有參數內的 Promise 都結束了, allSettled 就會 resolve,目前這草案還在 stage 1,過幾天的會議有望升到 stage 2,不過這是題外話。

最後一個想說的就是 await 處理 rejected Promise 的問題,如果是從 jQuery 時期就開始寫 Deferred/Promise 的人,可能會很習慣的把 Promise 的兩種狀態拿來當成值的一部份,事實上這也是 jQuery.ajax 的設計,如果用這種想法來寫 await 接值的時候,就會覺得很難處理 rejected 的狀態,因為要用 try...catch

async function wow () {
  try {
    const a = await fetch('/a');     
  } catch (error) {
    // deal with non-ok fetch
  }
}

要這樣寫還不如用舊的 .then 來接看起來還漂亮一點。不過實際上,這是錯誤的理解 Promise,Promise 不是用來取得兩種狀態用的,而是用來非同步取得單一個數值用的機制,而所謂 rejected 的狀態,其實就是發生非預期狀況(unexpected exception)的情形,這也就是為什麼 ECMAScript 版的 Promise 是用 throw Error 的方式來 reject Promise。

我一直覺得用 HTTP 請求來比較這兩種設計蠻好理解的,使用 jQuery 的 ajax ,server 端回非 200 的 status 的話,就會被當成是錯誤,然後回傳的 Promise 就會被 reject,但是在使用 ECMAScript Promise 的 fetch 中,不管 server 端回應的 status code,fetch 都會 resolve,而會 reject 的情形,就只有網路有問題的時候,像是網路斷線、存取被拒絕(CORS)等完全碰不到遠端主機的情形,也就是對於一個 HTTP 請求來說,真正的非預期狀況,所以如果你有兩種狀況要處理,那應該是回傳值的一部份,後面再用 if...else 來做分支。

回來看 await 的使用,究竟應該什麼時候來用 try...catch 呢,我自己有一個很簡單的初步判斷條件,就是這個取值的程式碼,如果不是非同步,沒有使用 await 的話,你會不會用 try...catch 包起來,不會的話,那改成非同步操作的程式碼應該也不用 try...catch 。不過現實世界當然還是比較難一點,非同步的取值風險和狀況還是比較多的,例如 fetch 遇到網路問題會 reject,但是還是需要處理這種狀況,不用 try...catch 的話,怎樣寫比較好呢?我的想法是,用 .then 處理好需要處理的情形,然後把結果包起來傳回去,所以要處理 fetch 的非預期狀況的話,就可以改成:

async function wow () {
  const a = await fetch('/a').then(null, error => {
    return {
      ok: false,
      status: -1,
      error: error,  
    };
  });
  
  if (a.status === -1) {
    // exception error handling
  }
}

這邊我設計成有非預期狀況時,status code 為 -1 ,並且把 error 資訊也傳回去,然後後面就可以直接拿來判斷是不是非預期狀況,當然也可以把這個處理包成一個自己的 myFetch

const myFetch = (url, options) =>
  fetch(url, options)
    .then(null, error => {
      ok: false,
      status: -1,
      error: error,  
    });

然後原來的程式就可以直接拿 myFetch 取代 fetch 了。

如果要通用一點的,其實有一個叫 await-to-js 的套件我覺得蠻不錯的,直接拿官方的範例看吧:

import to from 'await-to-js';

async function asyncTaskWithCb(cb) {
  let [err, user] = await to(UserModel.findById(1));
  if (!user) return cb('No user found');
}

它可以包裝 Promise 物件,然後不管那個 Promise 成功還是失敗,它自己都會 resolve,resolve 的值就是 [error, value] 這樣形式的陣列,一來符合 node 的 error-first callbacks ,再來就是配合 destructuring assignment 其實程式碼是蠻漂亮的。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

穿越计算机的迷雾

穿越计算机的迷雾

李忠 / 电子工业出版社 / 2011-1 / 36.00元

《穿越计算机的迷雾》从最基本的电学知识开始,带领读者一步一步、从无到有地制造一台能全自动工作的计算机。在这个过程中,读者可以学习到大量有趣的电学、数学和逻辑学知识,了解到它们是如何为电子计算机的产生创造条件,并促使它不断向着更快、更小、更强的方向发展。通过阅读《穿越计算机的迷雾》,读者可以很容易地理解自动计算实际上是如何发生的,而现代的计算机又是怎么工作的。以此为基础,在《穿越计算机的迷雾》的后面......一起来看看 《穿越计算机的迷雾》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换