非同步的JavaScript
從未如何簡單!過去段時間,我們使用回撥。然後,我們使用promises
。現在,我們有了非同步功能函式。
非同步函式能夠使得(我們)編寫非同步JavaScript更加容易,但是,它自帶一套陷阱,對初學者很不友好。
在這個由兩部分組成的文章中,我想分享下你需要了解的有關非同步函式的內容。【PS:另一部分暫不打算翻譯】
非同步功能
非同步功能函式包含async
關鍵詞。你可以在正常的函式宣告中使用它:
async function functionName (arguments) {
// Do something asynchronous
}
複製程式碼
你也可以使用箭頭函式。
const functionName = async (arguments) => {
// Do something asynchronous
}
複製程式碼
非同步函式總是返回promises
(非同步函式)它不管你返回什麼。其返回值都是promise
。
const getOne = async _ => {
return 1
}
const promise = getOne()
console.log(promise) // Promise
複製程式碼
筆記:在接著往前讀之前,你應該知道什麼是JavaScript Promises知識點,以及如何使用它們。否則,它會開始變得混亂。這篇文章會幫助你熟悉JavaScript Promise。
await關鍵字
當你呼叫promise
時,你會在then
中處理下一步,如下:
const getOne = async _ => {
return 1
}
getOne()
.then(value => {
console.log(value) // 1
})
複製程式碼
await
關鍵字允許你等待promise去解析。一旦解析完promise,它就會返回引數傳遞給then
呼叫。
const test = async _ => {
const one = await getOne()
console.log(one) // 1
}
test()
複製程式碼
返回await
在返回承諾(promise)之前沒有必要等待(await)。你可以直接退回承諾。
如果你
return await
些內容,則你首先是解決了原先promise。然後,你從已經解析的內容(resolved value)建立新的promise。return await
真的沒做什麼有效的東西。無需額外的步驟。
// Don't need to do this
const test = async _ => {
return await getOne()
}
test()
.then(value => {
console.log(value) // 1
})
複製程式碼
// Do this instead
const test = async _ => {
return getOne()
}
test()
.then(value => {
console.log(value) // 1
})
複製程式碼
注意:如果你不需要
await
,則不需要使用非同步功能(async function)。上面的例子可以改寫如下:
// Do this instead
const test = _ => {
return getOne()
}
test()
.then(value => {
console.log(value) // 1
})
複製程式碼
處理錯誤
如果一個promise出錯了,你可以使用catch呼叫來處理它,如下所示:
const getOne = async (success = true) => {
if (success) return 1
throw new Error('Failure!')
}
getOne(false)
.catch(error => console.log(error)) // Failure!
複製程式碼
如果你想在一個非同步函式中處理錯誤,你需要呼叫try/catch
。
const test = async _ => {
try {
const one = await getOne(false)
} catch (error) {
console.log(error) // Failure!
}
}
test()
複製程式碼
如果你有多個await
關鍵字,錯誤處理可能變得很難看...
const test = async _ => {
try {
const one = await getOne(false)
} catch (error) {
console.log(error) // Failure!
}
try {
const two = await getTwo(false)
} catch (error) {
console.log(error) // Failure!
}
try {
const three = await getThree(false)
} catch (error) {
console.log(error) // Failure!
}
}
test()
複製程式碼
還有更好的方法。
我們知道非同步函式總是返回一個promise。當我們呼叫promise時,我們可以在catch
呼叫中處理錯誤。這意味著我們可以通過新增.catch
來處理非同步函式中的任何錯誤。
const test = async _ => {
const one = await getOne(false)
const two = await getTwo(false)
const three = await getThree(false)
}
test()
.catch(error => console.log(error)))
複製程式碼
注意:Promise的
catch
方法只允許你捕獲一個錯誤。
多個awaits
await
阻止JavaScript執行下一行程式碼,直到promise解析為止。這可能會導致程式碼執行速度減慢的意外效果。
為了實際演示這點,我們需要在解析promise之前建立一個延遲。我們可以使用sleep
功能來建立延遲。
const sleep = ms => {
return new Promise(resolve => setTimeout(resolve, ms))
}
複製程式碼
ms
是解析前等待的毫秒數。如果你傳入1000
到sleep
函式,JavaScript將等待一秒才能解析promise。
// Using Sleep
console.log('Now')
sleep(1000)
.then(v => {
console.log('After one second')
})
複製程式碼
假設getOne
需要一秒來解析。為了建立這個延遲,我們將1000
(一秒)傳入到sleep
。一秒過後,sleep
promise解析後,我們返回值1。
const getOne = _ => {
return sleep(1000).then(v => 1)
}
複製程式碼
如果你使用await getOne()
,你會發現在getOne
解析之前需要一秒鐘。
const test = async _ => {
console.log('Now')
const one = await getOne()
console.log(one)
}
test()
複製程式碼
現在,假設你需要處理三個promises。每個promise都有一秒鐘的延遲。
const getOne = _ => {
return sleep(1000).then(v => 1)
}
const getTwo = _ => {
return sleep(1000).then(v => 2)
}
const getThree = _ => {
return sleep(1000).then(v => 3)
}
複製程式碼
如果你連續await
這三個promises,你將要等待三秒才能解析完所有promises。這並不好,因為我們強迫JavaScript在做我們需要做的事情之前等待了兩秒鐘。
const test = async _ => {
const one = await getOne()
console.log(one)
const two = await getTwo()
console.log(two)
const three = await getThree()
console.log(three)
console.log('Done')
}
test()
複製程式碼
如果getOne
,getTwo
和getThree
可以同時獲取,你將節省兩秒鐘。你可以使用Promise.all
同時獲取這三個promises。
有三個步驟:
- 建立三個promises
- 將三個promises新增到一個陣列中
- 使用
Promise.all
來await
promises陣列
如下所示:
const test = async _ => {
const promises = [getOne(), getTwo(), getThree()]
console.log('Now')
const [one, two, three] = await Promise.all(promises)
console.log(one)
console.log(two)
console.log(three)
console.log('Done')
}
test()
複製程式碼
這就是你需要了解的基本非同步功能函式!我希望這篇文章為你掃除了些障礙。
筆記:這篇文章是Learn JavaScript的修改摘錄。如果你發現本文有用,你可能需要去檢視它。
後話
至此,系列文callbacks,promises和async/await共三篇文章已經完成~