譯者按: 在Async/Await替代Promise的6個理由中,咱們比較了兩種不一樣的異步編程方法:Async/Await和Promise,這篇博客將經過示例代碼介紹Async/Await是如何簡化JavaScript代碼的。javascript
爲了保證可讀性,本文采用意譯而非直譯。另外,本文版權歸原做者全部,翻譯僅用於學習。java
Async/Await是JavaScript的ES7新特性,來源於**.NET和C#。它能夠不用回調函數,像同步代碼那些編寫異步代碼。這篇博客將經過一些代碼示例,來講明Async/Await**如何簡化JavaScript代碼。node
運行本文的示例代碼,並不須要額外的函數庫。對於最新版的主流瀏覽器中,例如Chrome,Firefox, Safari以及Edge,它們都支持Async/Await語法。另外,Node.js 7.6+也支持了Async/Await語法。編程
咱們編寫了一些簡單的API接口,用於模擬異步操做。這些接口都返回Promise,並在200ms後resolve一些數據。api
class Api { constructor () { this.user = { id: 1, name: 'test' } this.friends = [ this.user, this.user, this.user ] this.photo = 'not a real photo' } getUser () { return new Promise((resolve, reject) => { setTimeout(() => resolve(this.user), 200) }) } getFriends (userId) { return new Promise((resolve, reject) => { setTimeout(() => resolve(this.friends.slice()), 200) }) } getPhoto (userId) { return new Promise((resolve, reject) => { setTimeout(() => resolve(this.photo), 200) }) } throwError () { return new Promise((resolve, reject) => { setTimeout(() => reject(new Error('Intentional Error')), 200) }) } }
function callbackHell () { const api = new Api() let user, friends api.getUser().then(function (returnedUser) { user = returnedUser api.getFriends(user.id).then(function (returnedFriends) { friends = returnedFriends api.getPhoto(user.id).then(function (photo) { console.log('callbackHell', { user, friends, photo }) }) }) }) }
曾經使用Promise編寫回調函數的開發者必定不會陌生,這樣一層層的嵌套代碼一般是這樣結尾的:數組
}) }) }) }
在回調函數中調用回調函數,一層層地嵌套,這就是所謂的「回調地獄」。在真實的代碼中,這樣的狀況並很多見,一般更爲複雜。promise
function promiseChain () { const api = new Api() let user, friends api.getUser() .then((returnedUser) => { user = returnedUser return api.getFriends(user.id) }) .then((returnedFriends) => { friends = returnedFriends return api.getPhoto(user.id) }) .then((photo) => { console.log('promiseChain', { user, friends, photo }) }) }
Promise的最佳特性之一,就是能夠在then回調函數中,return一個新的Promise,這樣就能夠將這些Promise連接起來,只有一層嵌套。鏈式Promise比嵌套Promise簡單不少,可是仍是不少冗餘。瀏覽器
不使用回調函數能夠嗎?固然能夠!使用Async/Await的話,7行代碼就能夠搞定。併發
async function asyncAwaitIsYourNewBestFriend () { const api = new Api() const user = await api.getUser() const friends = await api.getFriends(user.id) const photo = await api.getPhoto(user.id) console.log('asyncAwaitIsYourNewBestFriend', { user, friends, photo }) }
使用await關鍵詞時,賦值操做將等到異步操做結束時才進行。這樣,看起來與同步代碼無異,實際執行事實上是異步的。異步
Async/Await可讓一些複雜操做,好比循環變得簡單。例如,當咱們須要獲取某個user的全部friends的friends列表,應該怎樣操做呢?
function promiseLoops () { const api = new Api() api.getUser() .then((user) => { return api.getFriends(user.id) }) .then((returnedFriends) => { const getFriendsOfFriends = (friends) => { if (friends.length > 0) { let friend = friends.pop() return api.getFriends(friend.id) .then((moreFriends) => { console.log('promiseLoops', moreFriends) return getFriendsOfFriends(friends) }) } } return getFriendsOfFriends(returnedFriends) }) }
咱們使用了遞歸函數getFriendsOfFriends來獲取friends-of-friends,知道friends數組爲空。如此簡單的任務,這樣寫顯然過於複雜了。
使用**Promise.all()**來實現的話,則並不是循環,而是併發執行。
This could be so much easier.
async function asyncAwaitLoops () { const api = new Api() const user = await api.getUser() const friends = await api.getFriends(user.id) for (let friend of friends) { let moreFriends = await api.getFriends(friend.id) console.log('asyncAwaitLoops', moreFriends) } }
這時,能夠直接使用for循環來實現,很是簡單。
使用循環逐個獲取friends-of-friends顯然太慢,採用併發方式更爲簡單。
async function asyncAwaitLoopsParallel () { const api = new Api() const user = await api.getUser() const friends = await api.getFriends(user.id) const friendPromises = friends.map(friend => api.getFriends(friend.id)) const moreFriends = await Promise.all(friendPromises) console.log('asyncAwaitLoopsParallel', moreFriends) }
爲了實現併發,只須要將Promise數組做爲Promise.all()的參數便可。這樣,只須要await一個Promise,而這個Promise會在全部併發操做結束時resolve。
function callbackErrorHell () { const api = new Api() let user, friends api.getUser().then(function (returnedUser) { user = returnedUser api.getFriends(user.id).then(function (returnedFriends) { friends = returnedFriends api.throwError().then(function () { console.log('Error was not thrown') api.getPhoto(user.id).then(function (photo) { console.log('callbackErrorHell', { user, friends, photo }) }, function (err) { console.error(err) }) }, function (err) { console.error(err) }) }, function (err) { console.error(err) }) }, function (err) { console.error(err) }) }
這樣作很是糟糕,代碼很是冗餘,可讀性也不好。
function callbackErrorPromiseChain () { const api = new Api() let user, friends api.getUser() .then((returnedUser) => { user = returnedUser return api.getFriends(user.id) }) .then((returnedFriends) => { friends = returnedFriends return api.throwError() }) .then(() => { console.log('Error was not thrown') return api.getPhoto(user.id) }) .then((photo) => { console.log('callbackErrorPromiseChain', { user, friends, photo }) }) .catch((err) => { console.error(err) }) }
這樣處理好多了,僅僅須要在Promise鏈的最後,使用catch方法處理全部錯誤。
async function aysncAwaitTryCatch () { try { const api = new Api() const user = await api.getUser() const friends = await api.getFriends(user.id) await api.throwError() console.log('Error was not thrown') const photo = await api.getPhoto(user.id) console.log('async/await', { user, friends, photo }) } catch (err) { console.error(err) } }
對於Async/Await代碼,使用Try/Catch便可處理,和同步代碼同樣,更加簡單。
如何你須要監控線上JavaScript代碼的錯誤時,能夠無償使用Fundebug的實時錯誤監控服務,只須要一行代碼就能夠搞定!
使用async關鍵詞定義的函數都會返回Promise,這樣能夠更方便地組織代碼。
例如,在以前的示例中,咱們能夠將獲取的user信息return,而不是直接打印;而後,咱們能夠經過返回的Promise來獲取user信息。
async function getUserInfo () { const api = new Api() const user = await api.getUser() const friends = await api.getFriends(user.id) const photo = await api.getPhoto(user.id) return { user, friends, photo } } function promiseUserInfo () { getUserInfo().then(({ user, friends, photo }) => { console.log('promiseUserInfo', { user, friends, photo }) }) }
使用Async/Await語法,則更加簡單:
async function awaitUserInfo () { const { user, friends, photo } = await getUserInfo() console.log('awaitUserInfo', { user, friends, photo }) }
如何獲取多個user的信息?
async function getLotsOfUserData () { const users = [] while (users.length < 10) { users.push(await getUserInfo()) } console.log('getLotsOfUserData', users) }
如何併發?如何處理錯誤?
async function getLotsOfUserDataFaster () { try { const userPromises = Array(10).fill(getUserInfo()) const users = await Promise.all(userPromises) console.log('getLotsOfUserDataFaster', users) } catch (err) { console.error(err) } }
版權聲明:
轉載時請註明做者Fundebug以及本文地址:
https://blog.fundebug.com/2017/10/16/async-await-simplify-javascript/