本文轉自做者Sandeep Dinesh的文章:Callbacks, Promises and Async/Awaitpromise
假設你有一個函數能夠在一段隨機的時間後打印一個字符串:bash
function printString(string){
setTimeout(
() => {
console.log(string)
},
Math.floor(Math.random() * 100) + 1
)
}
複製代碼
讓咱們嘗試按順序打印字母A,B,C:dom
function printAll(){
printString("A")
printString("B")
printString("C")
}
printAll()
複製代碼
每次調用printAll時,您會注意到A,B和C以不一樣的隨機順序打印!異步
這是由於這些函數是異步的。每一個函數按順序執行,但每一個函數都獨立於它本身的setTimeout。在開始以前,他們不會等待最後一個功能完成。async
這很是煩人,因此讓咱們用回調修復它。函數
回調是傳遞給另外一個函數的函數。第一個函數完成後,它將運行第二個函數。ui
function printString(string, callback){
setTimeout(
() => {
console.log(string)
callback()
},
Math.floor(Math.random() * 100) + 1
)
}
複製代碼
你能夠看到,修改原始函數是很是容易的,可使用回調。spa
再次,讓咱們嘗試按順序打印字母A,B,C:code
function printAll(){
printString("A", () => {
printString("B", () => {
printString("C", () => {})
})
})
}
printAll()
複製代碼
嗯,代碼如今很醜陋,但至少它有效!每次調用printAll時,都會獲得相同的結果。對象
回調的問題是它建立了一個名爲「Callback Hell」的東西。基本上,你開始在函數內的函數內嵌套函數,而且開始變得很是難以閱讀代碼。
Promise嘗試修復這個嵌套問題。讓咱們改變咱們的功能來使用Promises
function printString(string){
return new Promise((resolve, reject) => {
setTimeout(
() => {
console.log(string)
resolve()
},
Math.floor(Math.random() * 100) + 1
)
})
}
複製代碼
你能夠看到它看起來仍然很是類似。您將整個函數包裝在Promise中,而不是調用回調,而是調用resolve(若是出現錯誤則拒絕)。該函數返回此Promise對象。
再次,讓咱們嘗試按順序打印字母A,B,C:
function printAll(){
printString("A")
.then(() => {
return printString("B")
})
.then(() => {
return printString("C")
})
}
printAll()
複製代碼
這被稱爲承諾鏈。您能夠看到代碼返回函數的結果(將是Promise),並將其發送到鏈中的下一個函數。
代碼再也不嵌套,但看起來仍然很混亂!
經過使用箭頭函數的功能,咱們能夠刪除「包裝器」功能。代碼變得更清晰,但仍然有不少沒必要要的括號:
function printAll(){
printString("A")
.then(() => printString("B"))
.then(() => printString("C"))
}
printAll()
複製代碼
Await基本上是Promises的語法糖。它使您的異步代碼看起來更像是同步/過程代碼,人類更容易理解。
該PRINTSTRING功能不自許的版本在全部改變。
再次,讓咱們嘗試按順序打印字母A,B,C:
async function printAll(){
await printString("A")
await printString("B")
await printString("C")
}
printAll()
複製代碼
是啊...。好多了!
您可能會注意到咱們對包裝函數printAll使用「async」關鍵字。這讓咱們的JavaScript知道咱們正在使用async / await語法,若是你想使用Await,這是必要的。這意味着你不能在全球範圍內使用Await; 它老是須要一個包裝函數。大多數JavaScript代碼都在函數內部運行,所以這不是什麼大問題。
該PRINTSTRING函數不返回任何東西,是獨立的,全部咱們關心的是順序。可是,若是您想獲取第一個函數的輸出,在第二個函數中執行某些操做,而後將其傳遞給第三個函數,該怎麼辦?
咱們不是每次都打印字符串,而是建立一個鏈接字符串並傳遞它的函數。
這裏是回調樣式:
function addString(previous, current, callback){
setTimeout(
() => {
callback((previous + ' ' + current))
},
Math.floor(Math.random() * 100) + 1
)
}
複製代碼
爲了使用它:
function addAll(){
addString('', 'A', result => {
addString(result, 'B', result => {
addString(result, 'C', result => {
console.log(result) // Prints out " A B C"
})
})
})
}
addAll()
複製代碼
不太好。
這是Promise風格:
function addString(previous, current){
return new Promise((resolve, reject) => {
setTimeout(
() => {
resolve(previous + ' ' + current)
},
Math.floor(Math.random() * 100) + 1
)
})
}
複製代碼
爲了使用它:
function addAll(){
addString('', 'A')
.then(result => {
return addString(result, 'B')
})
.then(result => {
return addString(result, 'C')
})
.then(result => {
console.log(result) // Prints out " A B C"
})
}
addAll()
複製代碼
使用箭頭函數意味着咱們可使代碼更好一些:
function addAll(){
addString('', 'A')
.then(result => addString(result, 'B'))
.then(result => addString(result, 'C'))
.then(result => {
console.log(result) // Prints out " A B C"
})
}
addAll()
複製代碼
這確定更具可讀性,特別是若是你向鏈添加更多,但仍然是一堆括號。
該功能與Promise版本保持一致。
而且爲了使用它:
async function addAll(){
let toPrint = ''
toPrint = await addString(toPrint, 'A')
toPrint = await addString(toPrint, 'B')
toPrint = await addString(toPrint, 'C')
console.log(toPrint) // Prints out " A B C"
}
addAll()
複製代碼
Yeah. SO MUCH BETTER~