本文轉自做者Sandeep Dinesh的文章:Callbacks, Promises and Async/Awaitpromise
假設你有一個函數能夠在一段隨機的時間後打印一個字符串:dom
function printString(string){ setTimeout( () => { console.log(string) }, Math.floor(Math.random() * 100) + 1 ) }
讓咱們嘗試按順序打印字母A,B,C:異步
function printAll(){ printString("A") printString("B") printString("C") } printAll()
每次調用printAll時,您會注意到A,B和C以不一樣的隨機順序打印!async
這是由於這些函數是異步的。每一個函數按順序執行,但每一個函數都獨立於它本身的setTimeout。在開始以前,他們不會等待最後一個功能完成。函數
這很是煩人,因此讓咱們用回調修復它。code
回調是傳遞給另外一個函數的函數。第一個函數完成後,它將運行第二個函數。對象
function printString(string, callback){ setTimeout( () => { console.log(string) callback() }, Math.floor(Math.random() * 100) + 1 ) }
你能夠看到,修改原始函數是很是容易的,可使用回調。ip
再次,讓咱們嘗試按順序打印字母A,B,C:字符串
function printAll(){ printString("A", () => { printString("B", () => { printString("C", () => {}) }) }) } printAll()
嗯,代碼如今很醜陋,但至少它有效!每次調用printAll時,都會獲得相同的結果。get
回調的問題是它建立了一個名爲「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~