阿里雲最近在作活動,低至2折,有興趣能夠看看:
https://promotion.aliyun.com/...
爲了保證的可讀性,本文采用意譯而非直譯。javascript
想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等着你!html
ES8 引入的 async/await
在 JavaScript 的異步編程中是一個極好的改進。它提供了使用同步樣式代碼異步訪問 resoruces
的方式,而不會阻塞主線程。然而,它們也存在一些坑及問題。在本文中,將從不一樣的角度探討 async/await
,並演示如何正確有效地使用這對兄弟。前端
從 MDN 能夠看出:java
async
函數返回的是一個 Promise 對象。async 函數(包含函數語句、函數表達式、Lambda表達式)會返回一個 Promise 對象,若是在函數中 return
一個直接量,async 會把這個直接量經過 Promise.resolve()
封裝成 Promise 對象。c++
若是 async 函數沒有返回值, 它會返回 Promise.resolve(undefined)
。git
從 MDN 瞭解到:github
await 等待的是一個表達式,這個表達式的計算結果是 Promise 對象或者其它值(換句話說,await 能夠等任意表達式的結果)。編程
若是它等到的不是一個 Promise 對象,那 await 表達式的運算結果就是它等到的東西。segmentfault
若是它等到的是一個 Promise 對象,await 就忙起來了,它會阻塞後面的代碼,等着 Promise 對象 resolve,而後獲得 resolve 的值,做爲 await 表達式的運算結果。promise
這就是 await 必須用在 async 函數中的緣由。async 函數調用不會形成阻塞,它內部全部的阻塞都被封裝在一個 Promise 對象中異步執行。
async/await
帶給咱們的最重要的好處是同步編程風格。讓咱們看一個例子:
很明顯,async/await
版本比 promise
版本更容易理解。若是忽略 await
關鍵字,代碼看起來就像任何其餘同步語言,好比 Python。
最佳的地方不只在於可讀性。async/await
到今天爲止,全部主流瀏覽器都徹底支持異步功能。
本地瀏覽器的支持意味着你沒必要轉換代碼。更重要的是,它便於調試。當在函數入口點設置斷點並跨過 await
行時,將看到調試器在 bookModel.fetchAll()
執行其任務時暫停片刻,而後它將移動到下一個.filter
行,這比 promise 代碼要簡單得多,在 promise 中,必須在 .filter
行上設置另外一個斷點。
另外一個不太明顯的優勢是 async
關鍵字。 async
聲明 getBooksByAuthorWithAwait()函數返回值確保是一個 promise,所以調用者能夠安全地使用 getBooksByAuthorWithAwait().then(...)
或await getBooksByAuthorWithAwait()
。 想一想下面的例子(很差的作法!):
在上述代碼中,getBooksByAuthorWithPromise
可能返回 promise(正常狀況下)或 null 值(異常狀況下),在異常狀況下,調用者不能調用 .then()
。有了async
聲明,這種狀況就不會出現了。
一些文章將 async/wait 與 Promise 進行了比較,並聲稱它是 JavaScript 下一代異步編程風格,對此做者深表異議。async/await
是一種改進,但它只不過是一種語法糖,不會徹底改變咱們的編程風格。
從本質上說,async 函數仍然是 promise。在正確使用 async 函數以前,你必須先了解 promise,更糟糕的是,大多數時候你須要在使用 promises 的同時使用 async 函數。
考慮上面示例中的 getBooksByAuthorWithAwait() 和getbooksbyauthorwithpromise() 函數。請注意,它們不只功能相同,並且具備徹底相同的接口!
這意味着 getbooksbyauthorwithwait() 將返回一個 promise,因此也可使用 .then(...)
方式來調用它。
嗯,這未必是件壞事。只有 await
的名字給人一種感受,「哦,太好了,能夠把異步函數轉換成同步函數了」,這其實是錯誤的。
那麼在使用 async/await
時可能會犯什麼錯誤呢?下面是一些常見的例子。
儘管 await
可使代碼看起來像是同步的,但實際它們仍然是異步的,必須當心避免太過串行化。
上述代碼在邏輯上看似正確的,然而,這是錯誤的。
await bookModel.fetchAll()
會等待 fetchAll()
直到 fetchAll()
返回結果。await authorModel.fetch(authorId)
被調用。注意,authorModel.fetch(authorId)
並不依賴於 bookModel.fetchAll()
的結果,實際上它們能夠並行調用!然而,用了 await,兩個調用變成串行的,總的執行時間將比並行版本要長得多得多。
下面是正確的方式:
更糟糕的是,若是你想要一個接一個地獲取項目列表,你必須依賴使用 promises:
簡而言之,你仍然須要將流程視爲異步的,而後使用 await
寫出同步的代碼。在複雜的流程中,直接使用 promise 可能更方便。
在 promise中,異步函數有兩個可能的返回值: resolved
和 rejected
。咱們能夠用 .then()
處理正常狀況,用 .catch()
處理異常狀況。然而,使用 async/await
方式的,錯誤處理可能比較棘手。
最標準的(也是做者推薦的)方法是使用 try...catch
語法。在 await
調用時,在調用 await
函數時,若是出現非正常情況就會拋出異常,await 命令後面的 promise 對象,運行結果多是 rejected,因此最好把await 命令放在 try...catch
代碼塊中。以下例子:
在捕捉到異常以後,有幾種方法來處理它:
catch
塊中使用任何 return
語句至關於使用 return undefined
,undefined 也是一個正常值。)throw errorr
,它容許你在 promise 鏈中使用 async getBooksByAuthorWithAwait()
函數(也就是說,能夠像getBooksByAuthorWithAwait().then(...).catch(error => ...) 處理錯誤); 或者能夠用 Error
對象將錯誤封裝起來,如 throw new Error(error)
,當這個錯誤在控制檯中顯示時,它將給出完整的堆棧跟蹤信息。return Promise.reject(error)
,這至關於 throw error
,因此不建議這樣作。使用 try...catch
的好處:
try ... catch
塊中包裝多個 await
調用來處理一個地方的錯誤。這種方法也有一個缺陷。因爲 try...catch
會捕獲代碼塊中的每一個異常,因此一般不會被 promise 捕獲的異常也會被捕獲到。好比:
運行此代碼,你將獲得一個錯誤 ReferenceError: cb is not defined
。這個錯誤是由console.log()打印出來的的,而不是 JavaScript 自己。有時這多是致命的:若是 BookModel 被包含在一系列函數調用中,其中一個調用者吞噬了錯誤,那麼就很難找到這樣一個未定義的錯誤。
另外一種錯誤處理方法是受到Go語言的啓發。它容許異步函數返回錯誤和結果。詳情請看這篇博客文章:
How to write async await without try-catch blocks in Javascript
簡而言之,你能夠像這樣使用異步函數:
[err, user] = await to(UserModel.findById(1));
做者我的不喜歡這種方法,由於它將 Go 語言的風格帶入到了 JavaScript 中,感受不天然。但在某些狀況下,這可能至關有用。
這裏介紹的最後一種方法就是繼續使用 .catch()
。
回想一下 await
的功能:它將等待 promise 完成它的工做。值得注意的一點是 promise.catch()
也會返回一個 promise ,因此咱們能夠這樣處理錯誤:
這種方法有兩個小問題:
ES7引入的 async/await
關鍵字無疑是對J avaScrip t異步編程的改進。它可使代碼更容易閱讀和調試。然而,爲了正確地使用它們,必須徹底理解 promise,由於 async/await
只不過是 promise 的語法糖,本質上仍然是 promise。
原文:
https://hackernoon.com/javasc...
你的點贊是我持續分享好東西的動力,歡迎點贊!
乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。
https://github.com/qq44924588...
我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!
關注公衆號,後臺回覆福利,便可看到福利,你懂的。