我最喜歡的網站之一是BerkshireHathaway.com--它簡單,有效,而且自1997年推出以來一直正常運行。更值得注意的是,在過去的20年中,這個網站頗有可能從未出現過錯誤。爲何?由於它都是靜態的。它自20多年前推出以來幾乎同樣。若是你預先擁有全部數據,那麼構建網站很是簡單。不幸的是,如今大多數網站都沒有。爲了彌補這一點,咱們發明了「模式」來處理爲咱們的應用程序提取外部數據。像大多數事情同樣,這些模式都隨着時間的推移而發生變化。在這篇文章中,咱們將分析這三種最多見的模式的優缺點,模式分別是回調(Callbacks),Promises,和Async/Await 並從歷史背景談論它們的意義和進展。javascript
讓咱們從這些數據獲取的最初的模式開始,回調(Callbacks)java
我假設你徹底不知道什麼是回調。若是我假設錯了,只需向下滾動一下跳過。git
當我第一次學習編程時,它幫助我將函數理解爲機器。這些機器能夠作任何你想要的東西。他們甚至能夠接受輸入並返回一個值。每臺機器上都有一個按鈕,你能夠在須要機器運行時按下該按鈕,即()。github
function add (x, y) {
return x + y
}
add(2,3) // 5 - 按下按鈕,執行機器
複製代碼
不管我按下按鈕,你按下按鈕,或者別人按下按鈕無所謂。不管什麼時候按下按鈕,機器都將運行。編程
function add (x, y) {
return x + y
}
const me = add
const you = add
const someoneElse = add
me(2,3) // 5 - Press the button, run the machine.
you(2,3) // 5 - Press the button, run the machine.
someoneElse(2,3) // 5 - Press the button, run the machine.
複製代碼
在上面的代碼,咱們分配add函數,三個不一樣的變量,me,you,和someoneElse。重要的是要注意add咱們建立的原始變量和每一個變量都指向內存中的相同位置。它們在不一樣的名稱下徹底相同。因此,當咱們調用me時you,或者someoneElse,就好像咱們正在調用同樣add函數。 如今若是咱們把add機器送到另外一臺機器怎麼辦?請記住,按下()按鈕並不重要,若是按下它,它就會運行。json
function add (x, y) {
return x + y
}
function addFive (x, addReference) {
return addReference(x, 5) // 15 - Press the button, run the machine.
}
addFive(10, add) // 15
複製代碼
你的大腦可能在這一點上有點奇怪,但這裏沒有新的東西。咱們不是「按下按鈕」 add,而是add做爲參數傳遞addFive,重命名它addReference,而後咱們「按下按鈕」或調用它。api
這突出了JavaScript語言的一些重要概念。首先,正如你能夠將字符串或數字做爲參數傳遞給函數同樣,你也能夠將函數的引用做爲參數傳遞。當執行此操做時,做爲參數傳遞的函數稱爲回調函數,而且將回調函數傳遞給的函數稱爲高階函數。數組
由於詞彙很重要,因此這裏的代碼與從新命名的變量相同,以匹配他們演示的概念。promise
function add (x,y) {
return x + y
}
function higherOrderFunction (x, callback) {
return callback(x, 5)
}
higherOrderFunction(10, add)
複製代碼
這種模式應該看起來很熟悉,無處不在。若是你曾經使用過任何JavaScript Array方法,那麼你已經使用了回調。若是你曾經使用過lodash,那麼你已經使用過回調。若是你曾經使用過jQuery,那麼你已經使用了回調。安全
[1,2,3].map((i) => i + 5)
_.filter([1,2,3,4], (n) => n % 2 === 0 );
$('#btn').on('click', () =>
console.log('Callbacks are everywhere')
)
複製代碼
一般,回調有兩種常見的用例。第一,咱們看下.map和_.filter例子,是翻轉一個值到另外一個很好的抽象。咱們說「嘿,這是一個數組和一個函數。來吧,根據我給你的函數給我一個新的值「。第二個,也就是咱們在jQuery示例中看到的,是將函數的執行延遲到特定時間。「嘿,這是這個函數。每當btn點擊具備id的元素時,請繼續調用它。「這是咱們將關注的第二個用例,」延遲執行函數直到特定時間「。
如今咱們只看了同步的例子。正如咱們在本文開頭所討論的那樣,咱們構建的大多數應用程序都沒有預先得到所需的全部數據。相反,他們須要在用戶與應用程序交互時獲取外部數據。咱們剛剛看到回調如何成爲一個很好的用例,由於它們再次容許你「延遲執行函數直到特定時間」。看看咱們如何使該句子適應數據提取並不須要太多想象力。咱們能夠延遲函數的執行,直到咱們得到所需的數據,而不是將函數的執行延遲到特定時間。這多是最流行的例子,jQuery的方法:getJSON。
// updateUI and showError are irrelevant.
// Pretend they do what they sound like.
const id = 'tylermcginnis'
$.getJSON({
url: `https://api.github.com/users/${id}`,
success: updateUI,
error: showError,
})
複製代碼
在得到用戶數據以前,咱們沒法更新應用的UI。那麼咱們該怎麼辦?咱們說,「嘿,這是一個對象。若是請求成功,請繼續調用success並傳遞用戶的數據。若是沒有,請繼續調用error並傳遞錯誤對象。你不須要擔憂每種方法的做用,只要確保在你應該的時候調用它們。這是使用異步請求回調的完美演示。
在這一點上,咱們已經瞭解了回調是什麼以及它們如何在同步和異步代碼中都有用處的。咱們尚未談到的是回調的黑暗面。請看下面的代碼。你能說出發生了什麼嗎?
// updateUI, showError, and getLocationURL are irrelevant.
// Pretend they do what they sound like.
const id = 'tylermcginnis'
$("#btn").on("click", () => {
$.getJSON({
url: `https://api.github.com/users/${id}`,
success: (user) => {
$.getJSON({
url: getLocationURL(user.location.split(',')),
success (weather) {
updateUI({
user,
weather: weather.query.results
})
},
error: showError,
})
},
error: showError
})
})
複製代碼
若是以爲有幫助,你能夠在這裏玩實時版本。
請注意,咱們添加了一些回調層。首先,咱們說在btn點擊具備id的元素以前不要運行初始的AJAX請求。單擊按鈕後,咱們會發出第一個請求。若是該請求成功,咱們會發出第二個請求。若是該請求成功,咱們將調用updateUI從兩個請求得到的數據的方法。不管你是否乍一看是否理解了代碼,客觀地說它比之前的代碼更難閱讀。這將咱們帶到「回調地獄」的主題。
做爲人類,咱們很天然地會順序思考。當你在嵌套回調中嵌套回調時,它會強迫你超出你天然的思惟方式。當你的軟件閱讀方式與天然思考方式之間存在脫節時,就會發生錯誤。
像大多數軟件問題的解決方案同樣,一種使「回調地獄」更容易消費的經常使用方法是模塊化你的代碼。
function getUser(id, onSuccess, onFailure) {
$.getJSON({
url: `https://api.github.com/users/${id}`,
success: onSuccess,
error: onFailure
})
}
function getWeather(user, onSuccess, onFailure) {
$.getJSON({
url: getLocationURL(user.location.split(',')),
success: onSuccess,
error: onFailure,
})
}
$("#btn").on("click", () => {
getUser("tylermcginnis", (user) => {
getWeather(user, (weather) => {
updateUI({
user,
weather: weather.query.results
})
}, showError)
}, showError)
})
複製代碼
若是以爲有幫助,你能夠在這裏玩實時版本。
好的,函數名稱能夠幫助咱們更加了解正在發生的事情,但客觀上是「更好」嗎?並非不少。咱們只是在回調地獄的可讀性問題上加了一個創可貼。問題仍然存在,咱們天然地按順序思考,即便有額外的功能,嵌套的回調也會使咱們擺脫順序的思惟方式。
下一期回調與控制反轉有關。當你編寫一個回調時,假設你給回調的程序是負責的,而且會在它應該的時候(而且只有當它)時調用它。其實是將程序控制權轉換爲另外一個程序。當您處理jQuery,lodash甚至vanilla JavaScript等庫時,能夠安全地假設使用正確的參數在正確的時間調用回調函數。可是,對於許多第三方庫,回調函數是您與它們交互方式的接口。第三方庫不管是故意的仍是偶然的,均可以打破他們與你的回調互動的方式,這是徹底合情合理的。
function criticalFunction () {
// It's critical that this function // gets called and with the correct // arguments. } thirdPartyLib(criticalFunction) 複製代碼
既然你不是那個調用者criticalFunction,你就能夠控制調用它的時間和參數。大多數時候這不是問題,可是當它出現問題時,這是一個很大的問題。
你有沒有預訂去過一個繁忙的餐館?當這種狀況發生時,餐廳須要一種方法在桌子打開時與你聯繫。從歷史上看,當你的桌子準備就緒時,他們只會取你的名字並大喊大叫。而後,天然而然地,他們決定開始變幻想。一個解決方案是,一旦桌子打開,他們就會取你的號碼並給你發短信,而不是取你的名字。這使您能夠超出大喊大叫的範圍,但更重要的是,它容許他們隨時根據須要定位你的手機廣告。聽起來有點熟?這應該!好吧,也許不該該。這是回調的隱喻!將你的號碼提供給餐館就像給第三方服務提供回撥功能同樣。你但願餐廳在桌子打開時給您發短信,就像你同樣指望第三方服務在什麼時候以及如何表達時調用你的功能。一旦你的號碼或回叫功能掌握在他們手中,您就失去了全部控制權。
值得慶幸的是,存在另外一種解決方案。一個設計,容許您保持全部控制。你甚至可能之前都經歷過 - 這是他們給你的小嗡嗡聲。你知道,這個。
蜂鳴器始終處於三種不一樣狀態中的一種- pending,fulfilled或rejected。
pending是默認的初始狀態。當他們給你蜂鳴器時,它處於這種狀態。
fulfilled 當蜂鳴器閃爍而且你的桌子準備就緒時蜂鳴器所在的狀態。
rejected當出現問題時,蜂鳴器處於狀態。也許餐廳即將關閉,或者他們忘了有人在晚上出租餐廳。
一樣,要記住的重要一點是,你,蜂鳴器的接收器,擁有全部的控制權。若是蜂鳴器進入fulfilled,你能夠去你的桌子。若是它被放入fulfilled而且你想忽略它,那麼很酷,你也能夠這樣作。若是它被放入rejected,那很糟糕,但你能夠去別的地方吃。若是沒有任何事情發生而且它留在pending,你永遠不會吃,但你實際上並無任何東西。
如今你已成爲餐廳蜂鳴器的主人,讓咱們將這些知識應用到重要的事情上。
若是給餐廳你的號碼就像給他們一個回調功能,接收這個小小的東西就像收到所謂的「Promise」。
一如既往,讓咱們從爲何開始吧。爲何Promises存在?它們的存在使得使異步請求更易於管理的複雜性。徹底像蜂鳴器,一個 Promise能夠處於三種狀態之一pending,fulfilled或者rejected。與蜂鳴器不一樣,它們表明表示餐館桌子狀態的這些狀態,它們表明異步請求的狀態。
若是異步請求仍在進行中,則Promise狀態爲pending。若是異步請求成功完成,則Promise狀態將更改成fulfilled。若是異步請求失敗,Promise則將更改成狀態rejected。蜂鳴器比喻頗有意義,對嗎?
既然你已經理解了Promise存在的緣由以及它們能夠存在的不一樣狀態,那麼咱們還須要回答三個問題。 一、如何創造一個Promise? 二、如何改變Prommise的狀態? 三、當Promise的狀態發生變化時,如何監聽?
這個很直接。建立一個new實例Promise。
const promise = new Promise()
複製代碼
該Promise構造函數接受一個參數,一個(回調)函數。這個函數將傳遞兩個參數,resolve和reject。
resolve - 一個容許你更改Promise狀態的功能 fulfilled
reject- 一個容許你更改Promise狀態的功能rejected。
在下面的代碼中,咱們使用setTimeout等待2秒而後調用resolve。這將改變Promise的狀態fulfilled。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve() // Change status to 'fulfilled'
}, 2000)
})
複製代碼
咱們能夠經過在建立它以後當即記錄promise來看到這種變化,而後resolve在調用以後大約2秒後再次記錄。
注意Promise從pending到resolved。
在我看來,這是最重要的問題。很酷咱們知道如何建立Promise並改變其狀態,但若是咱們在狀態發生變化後不知道如何作任何事情,那就毫無價值。
咱們尚未談到的一件事是Promise其實是什麼。當你建立一個時new Promise,你真的只是建立一個普通的舊JavaScript對象。該對象能夠調用兩個方法then,和catch。這是關鍵。當promise的狀態更改fulfilled爲時,.then將調用傳遞給的函數。當promise的狀態更改rejected爲時,.catch將調用傳遞給的函數。這意味着一旦你建立了一個promise,若是異步請求成功,你將傳遞你想要運行的函數.then。若是異步請求失敗,你將傳遞要運行的功能.catch。
咱們來看一個例子吧。咱們將setTimeout再次使用fulfilled在兩秒鐘(2000毫秒)以後將Promise的狀態更改成。
function onSuccess () {
console.log('Success!')
}
function onError () {
console.log('💩')
}
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 2000)
})
promise.then(onSuccess)
promise.catch(onError)
複製代碼
若是運行上面的代碼,你會注意到大約2秒後,您將在控制檯中看到「成功!」。這種狀況再次發生的緣由是兩件事。首先,當咱們建立了promise時,咱們resolve在〜2000毫秒以後調用- 這改變了promise的狀態fulfilled。其次,咱們將onSuccess函數傳遞給promises的.then方法。經過這樣作,咱們告訴Promise,onSuccess當Promise的狀態改變爲fulfilled〜2000毫秒後它所作的時,調用。
如今讓咱們僞裝發生了一些很差的事情,咱們想要改變Promise的狀態rejected。resolve咱們打電話,而不是打電話reject。
function onSuccess () {
console.log('Success!')
}
function onError () {
console.log('💩')
}
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
reject()
}, 2000)
})
promise.then(onSuccess)
promise.catch(onError)
複製代碼
如今這一次而不是onSuccess被調用的onError函數,由於咱們調用了函數reject。
你已經瞭解了Promise API的方法,讓咱們開始查看一些真實的代碼。
還記得咱們以前看到的最後一個異步回調示例嗎?
function getUser(id, onSuccess, onFailure) {
$.getJSON({
url: `https://api.github.com/users/${id}`,
success: onSuccess,
error: onFailure
})
}
function getWeather(user, onSuccess, onFailure) {
$.getJSON({
url: getLocationURL(user.location.split(',')),
success: onSuccess,
error: onFailure,
})
}
$("#btn").on("click", () => {
getUser("tylermcginnis", (user) => {
getWeather(user, (weather) => {
updateUI({
user,
weather: weather.query.results
})
}, showError)
}, showError)
})
複製代碼
咱們能夠在這裏使用Promise API而不是使用回調嗎?若是咱們將AJAX請求包含在promise中,該怎麼辦?而後咱們能夠簡單地resolve或reject取決於請求的方式。讓咱們開始吧getUser。
function getUser(id) {
return new Promise((resolve, reject) => {
$.getJSON({
url: `https://api.github.com/users/${id}`,
success: resolve,
error: reject
})
})
}
複製代碼
很好。請注意,參數getUser已更改。而不是接收id,onSuccess和onFailure,它只是接收id。再也不須要那些其餘兩個回調函數,由於咱們再也不反轉控制。相反,咱們使用Promise resolve和reject函數。resolve若是請求成功reject將被調用,若是有錯誤將被調用。
接下來讓咱們重構一下getWeather。咱們將遵循相同的策略。咱們將使用使用resolve和reject而不是接收onSuccess和onFailure回調函數。
function getWeather(user) {
return new Promise((resolve, reject) => {
$.getJSON({
url: getLocationURL(user.location.split(',')),
success: resolve,
error: reject,
})
})
}
複製代碼
看起來不錯。如今咱們須要更新的最後一件事是咱們的點擊處理程序。請記住,這是咱們想要採起的流程。
從Github API獲取用戶的信息。 一、使用用戶的位置從Yahoo Weather API獲取他們的天氣。 二、使用用戶信息及其天氣更新UI。 三、讓咱們從#1開始 - 從Github API獲取用戶的信息。
$("#btn").on("click", () => {
const userPromise = getUser('tylermcginnis')
userPromise.then((user) => {
})
userPromise.catch(showError)
})
複製代碼
請注意,如今它不是getUser接受兩個回調函數,而是返回一個咱們能夠調用.then和.catch啓用的Promise。若是.then被調用,將使用用戶的信息調用它。若是.catch被調用,它將被調用錯誤。
接下來讓咱們作#2 - 使用用戶的位置來獲取他們的天氣。
$("#btn").on("click", () => {
const userPromise = getUser('tylermcginnis')
userPromise.then((user) => {
const weatherPromise = getWeather(user)
weatherPromise.then((weather) => {
})
weatherPromise.catch(showError)
})
userPromise.catch(showError)
})
複製代碼
請注意,咱們遵循咱們在#1中徹底相同的模式,但如今咱們調用getWeather它傳遞給user咱們的對象userPromise。
最後,#3 - 使用用戶信息及其天氣更新UI。
$("#btn").on("click", () => {
const userPromise = getUser('tylermcginnis')
userPromise.then((user) => {
const weatherPromise = getWeather(user)
weatherPromise.then((weather) => {
updateUI({
user,
weather: weather.query.results
})
})
weatherPromise.catch(showError)
})
userPromise.catch(showError)
})
複製代碼
這是你可使用的完整代碼。 新代碼看起來更好,但咱們仍然能夠作出一些改進。在咱們作出這些改進以前,你須要知道Promise到兩個功能:鏈式調用和將參數從resolve傳遞到then。
.then和.catch都會返回一個新的Promise。這彷佛是一個小細節,但它很重要,由於它意味着Promise能夠被鏈式調用。
在下面的示例中,咱們調用getPromise它返回一個將在至少2000毫秒內解析的promise。從那裏,由於.then將返回一個Promise,咱們能夠繼續將咱們的.thens連接在一塊兒,直到咱們拋出一個new Error被該.catch方法捕獲的東西。
function getPromise () {
return new Promise((resolve) => {
setTimeout(resolve, 2000)
})
}
function logA () {
console.log('A')
}
function logB () {
console.log('B')
}
function logCAndThrow () {
console.log('C')
throw new Error()
}
function catchError () {
console.log('Error!')
}
getPromise()
.then(logA) // A
.then(logB) // B
.then(logCAndThrow) // C
.catch(catchError) // Error!
複製代碼
很酷,但爲何這麼重要?請記住,在回調部分,咱們談到了回調的一個缺點,即它們會迫使你擺脫天然,順序的思惟方式。當你將Promise連接在一塊兒時,它並不會強迫你擺脫那種天然的思惟方式,由於鏈式調用Promise是連續的。getPromise runs then logA runs then logB runs then...。
這樣你就能夠看到另外一個示例,這是使用fetchAPI 時的常見用例。fetch將返回一個將經過HTTP響應解決的Promise。要得到實際的JSON,你須要調用.json。因爲連接,咱們能夠按順序思考這個問題。
fetch('/api/user.json')
.then((response) => response.json())
.then((user) => {
// user is now ready to go.
})
複製代碼
如今咱們知道有關鏈式調用,讓咱們來重構咱們早期getUser/ getWeather代碼而且使用它。
function getUser(id) {
return new Promise((resolve, reject) => {
$.getJSON({
url: `https://api.github.com/users/${id}`,
success: resolve,
error: reject
})
})
}
function getWeather(user) {
return new Promise((resolve, reject) => {
$.getJSON({
url: getLocationURL(user.location.split(',')),
success: resolve,
error: reject,
})
})
}
$("#btn").on("click", () => {
getUser("tylermcginnis")
.then(getWeather)
.then((weather) => {
// We need both the user and the weather here.
// Right now we just have the weather
updateUI() // ????
})
.catch(showError)
})
複製代碼
它看起來好多了,但如今咱們遇到了一個問題。你能發現它嗎?在第二個.then咱們要調用updateUI。問題是,咱們須要經過updateUI這兩個user和weather。目前咱們如何設置,咱們只收到weather,而不是user。不知何故,咱們須要找出一種方法來使它成爲一個Promise,即getWeather使用user和來解決回報weather。
這是關鍵。resolve只是一個功能。您傳遞給它的任何參數都將傳遞給給定的函數.then。這是什麼意思是,裏面getWeather,若是咱們調用resolve咱們本身,咱們能夠經過它weather和user。而後,.then咱們鏈中的第二個方法將同時接收user和weather做爲參數。
function getWeather(user) {
return new Promise((resolve, reject) => {
$.getJSON({
url: getLocationURL(user.location.split(',')),
success(weather) {
resolve({ user, weather: weather.query.results })
},
error: reject,
})
})
}
$("#btn").on("click", () => {
getUser("tylermcginnis")
.then(getWeather)
.then((data) => {
// Now, data is an object with a
// "weather" property and a "user" property.
updateUI(data)
})
.catch(showError)
})
複製代碼
你能夠在這裏玩最終的代碼 它位於咱們的點擊處理程序中,與回調相比,你真正看到了Promise的力量。
// Callbacks 🚫
getUser("tylermcginnis", (user) => {
getWeather(user, (weather) => {
updateUI({
user,
weather: weather.query.results
})
}, showError)
}, showError)
// Promises ✅
getUser("tylermcginnis")
.then(getWeather)
.then((data) => updateUI(data))
.catch(showError);
複製代碼
遵循這種邏輯感受很天然,由於它是咱們習慣於按順序思考的方式。getUser then getWeather then update the UI with the data。 如今很明顯,promises會大大提升異步代碼的可讀性,但有沒有辦法讓它變得更好?假設你是TC39委員會成員,而且你有能力爲JavaScript語言添加新功能。你將採起哪些步驟來改進此代碼?
$("#btn").on("click", () => {
getUser("tylermcginnis")
.then(getWeather)
.then((data) => updateUI(data))
.catch(showError)
})
複製代碼
正如咱們所討論的那樣,代碼讀得很是好。正如咱們的大腦工做同樣,它是按順序排列的。咱們遇到的一個問題是咱們須要將data(users)從第一個異步請求一直到最後一個.then。這不是什麼大不了的事,但它讓咱們改變了咱們的getWeather功能,也傳遞了它users。若是咱們編寫異步代碼的方式與編寫同步代碼的方式相同怎麼辦?若是咱們這樣作了,那麼這個問題就會完全消失,並且它仍會按順序讀取。這是一個想法。
$("#btn").on("click", () => {
const user = getUser('tylermcginnis')
const weather = getWeather(user)
updateUI({
user,
weather,
})
})
複製代碼
好吧,那會很好。咱們的異步代碼看起來就像咱們的同步代碼。咱們的大腦沒有額外的步驟須要採起,由於咱們已經很是熟悉這種思惟方式。可悲的是,這顯然是行不通的。如你所知,若是咱們要運行上面的代碼,user而且weather二者都只是Promise,由於那是什麼getUser並getWeather返回。但請記住,咱們正在使用TC39。咱們有能力爲咱們想要的語言添加任何功能。按原樣,這段代碼在製做工做時會很是棘手。咱們必須以某種方式教JavaScript引擎,以便了解異步函數調用和常規的同步函數調用之間的區別。讓咱們在代碼中添加一些關鍵字,以便在引擎上更輕鬆。
首先,讓咱們在主函數自己添加一個關鍵字。這可讓引擎知道這個函數內部咱們將要進行一些異步函數調用。讓咱們用async它。
$("#btn").on("click", async () => {
const user = getUser('tylermcginnis')
const weather = getWeather(user)
updateUI({
user,
weather,
})
})
複製代碼
酷。這看似合理。接下來讓咱們添加另外一個關鍵字,讓引擎確切知道被調用的函數什麼時候是異步的而且將返回一個promise。咱們來使用await。如同,「嘿發動機。此函數是異步的並返回一個promise。不要像往常同樣繼續,繼續「等待」Promise的最終的值並在繼續以前將其返回「。與這兩個咱們的新的async和await在遊戲中的關鍵字,咱們的新的代碼看起來像這樣。
$("#btn").on("click", async () => {
const user = await getUser('tylermcginnis')
const weather = await getWeather(user.location)
updateUI({
user,
weather,
})
})
複製代碼
很是漂亮。咱們已經發明瞭一種合理的方法來使咱們的異步代碼看起來和行爲就像它是同步的同樣。如今下一步是讓TC39上的某我的相信這是一個好主意。幸運的是,正如你如今可能已經猜到的那樣,咱們不須要作任何使人信服的事情,由於這個功能已經成爲JavaScript的一部分並且它被稱爲Async/Await。
不相信我?這是咱們的實時代碼,如今咱們已經添加了Async / Await。隨意玩它。
既然您已經看到了Async / Await的好處,那麼讓咱們討論一些重要的細節,這些細節很重要。首先,不管什麼時候添加async到函數,該函數都將隱式返回一個promise。
async function getPromise(){}
const promise = getPromise()
複製代碼
即便getPromise字面上是空的,它仍然會返回一個Promise,由於它是一個async函數。
若是async函數返回一個值,那麼該值也將包含在一個promise中。這意味着你將不得不使用.then它來訪問它。
async function add (x, y) {
return x + y
}
add(2,3).then((result) => {
console.log(result) // 5
})
複製代碼
若是你嘗試await在非函數內部使用關鍵字async,則會出現錯誤。
$("#btn").on("click", () => {
const user = await getUser('tylermcginnis') // SyntaxError: await is a reserved word
const weather = await getWeather(user.location) // SyntaxError: await is a reserved word
updateUI({
user,
weather,
})
})
複製代碼
如下是我對此的見解。添加async到函數時,它會執行兩項操做。它使得函數自己返回(或包裝返回的內容)一個Promise並使它能夠await在其內部使用
你可能已經注意到咱們有點做弊。在咱們的原始代碼中,咱們有一種方法能夠捕獲任何錯誤.catch。當咱們切換到Async / Await時,咱們刪除了該代碼。使用Async / Await,最多見的方法是將代碼包裝在一個try/catch塊中以便可以捕獲錯誤。
$("#btn").on("click", async () => {
try {
const user = await getUser('tylermcginnis')
const weather = await getWeather(user.location)
updateUI({
user,
weather,
})
} catch (e) {
showError(e)
}
})
複製代碼
(完)
以上譯文僅用於學習交流,水平有限,不免有錯誤之處,敬請指正。