概述
promise
為 js 提供了併發非同步能力。
promise.all
可以等待一批 promise
任務 全部執行完成
後返回 結果集合
。
async/await
可以使一批 promise
按 同步序列
的模式去執行(一些介面依賴場景是有此需求的)。
Promise / Promise.all
Promise
提供了非同步介面,搭配 resolve/reject
可以很方便的讓業務程式碼併發執行,但同時也產生了 回撥地獄
問題。
Promise.all
則是併發執行任務集合,且等待所有的任務執行完成後,一併返回結果集。
function promiseReq(url) {
return new Promise((resolve, reject) => {
fetch(url).then((res) => {
resolve(res)
}).catch((e) => {
reject(e)
})
})
}
promiseReq(url)
.then(res => console.log(res))
.catch(e => console.error(e))
async / await
async
正如其名,用於定義一個非同步方法,它會自動將方法封裝成一個 Promise
返回,並且使用 return
代表 resolve
,throw
代表 reject
。
async function asyncFoo() {
if (Math.floor(Math.random() * 10) > 5) {
return "asyncFoo resolved"
}
throw "asyncFoo rejected"
}
console.log(asyncFoo() instanceof Promise)
asyncFoo().then((res) => {
console.log(res)
}).catch((e) => {
console.error(e)
})
但日常開發中我們並不會單獨使用 async
,而是搭配 await
去同步多個 promise
任務。await
的作用就是讓 Promise
的非同步回撥降維至同步模式。
場景1 - 介面併發執行且資料結果彙總
當需要等待一批介面的資料全部返回後,才可以繼續執行後面的介面時,則可以使用 Promise.all
來處理 一批任務
。其會 併發
執行 任務集合
中的 Promise 任務
,等待所有的任務執行完成後,彙總結果並返回(結果陣列)。
function promiseEnum(countDown) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("promiseEnum countDown " + countDown)
}, countDown * 1000)
})
}
Promise.all([promiseEnum(1), promiseEnum(2), promiseEnum(3)])
.then((values) => {
// 等待 1 2 3 併發執行完成
console.log("Promise.all values", values)
promiseEnum(4).then((res4) => {
console.log(res4)
})
})
場景2 - 介面順序依賴執行
試想一下,有4個資料介面,後者依賴前者返回的結果才能繼續執行,如果使用傳統的 Promise
,那大概就是
// callback hell
promiseReq(url1).then((res1) => {
promiseReq(url2 + res1).then((res2) => {
promiseReq(url3 + res2).then((res3) => {
promiseReq(url4 + res3).then((res4) => {
console.log("promiseReq finished")
}).catch(err => console.error(err))
}).catch(err => console.error(err))
}).catch(err => console.error(err))
}).catch(err => console.error(err))
使用 async/await
則可以友好的解決依賴回撥問題,讓程式碼以 同步
的風格編寫和執行。
// 使用 async/await 則可以友好的解決依賴回撥問題
async function promiseSyncReq() {
let res1 = await promiseReq(url1)
let res2 = await promiseReq(url2 + res1)
let res3 = await promiseReq(url3 + res2)
let res4 = await promiseReq(url4 + res3)
return res4
}
promiseSyncReq().then((res4) => {
console.log(res4)
}).catch(e => console.log(err))
async/await
的執行耗時等於各個 Prmoise
累計的總耗時(執行流本就是要序列,耗時自然是各介面的累計,回撥模式也一樣的)。
async function awaitAllPromise() {
let res1 = await promiseEnum(1)//阻塞等待 耗時1秒
let res2 = await promiseEnum(2)//阻塞等待 耗時2秒
let res3 = await promiseEnum(3)//阻塞等待 耗時3秒
let res4 = await promiseEnum(4)//阻塞等待 耗時4秒
//執行總耗時為各 Promise 耗時的累加
return [res1, res2, res3, res4]
}
組合使用
async/await
與 promise/promise.all
組合使用,靈活實現 部分任務併發執行
, 部分任務同步執行
。
async function batchExec() {
console.group("batchExec")
let startTime = new Date().getTime()
console.log("batchExec start", startTime)
// 等待 1,2,3 併發執行完成
let paralValues = await Promise.all([promiseEnum(1), promiseEnum(2), promiseEnum(3)])
let paralTime = new Date().getTime()
console.log("parallel 1,2,3 finished", paralTime, (paralTime - startTime) / 1000, paralValues)
// 再繼續執行 4
let res4 = await promiseEnum(4)
let endTime = new Date().getTime()
console.log("batchExec end", endTime, (endTime - startTime) / 1000)
console.groupEnd("batchExec")
return [...paralValues, res4]
}
batchExec()
.then(res => console.log(res))
.catch(e => console.error(e))