async/await

前言

異步操作一直是JS中不可或缺的一環(huán),從最開始回調(diào)函數(shù),到后面的Promise,再到ES2017引入的async函數(shù),異步操作逐漸進(jìn)化,變得越來越簡(jiǎn)單方便,接下來就仔細(xì)看看在ES2017引入了async函數(shù)后,異步操作產(chǎn)生了哪些變化。

有什么用

以往我們使用異步函數(shù),都是async/await一起用的,但是這回我準(zhǔn)備拆開看,分別介紹async和await有什么用

async作用

通常情況下使用async命令是因?yàn)楹瘮?shù)內(nèi)部有await命令,因?yàn)閍wait命令只能出現(xiàn)在async函數(shù)里面,否則會(huì)報(bào)語法,這就是為什么async/await成對(duì)出現(xiàn)的原因,但是如果對(duì)一個(gè)普通函數(shù)單獨(dú)加個(gè)async會(huì)是什么結(jié)果呢?來看個(gè)例子:

async function test () {
  let a = 2
  return a
}
const res = test()
console.log(res)

由例子可以async函數(shù)返回的是一個(gè)Promise對(duì)象,如果函數(shù)中有返回值,async會(huì)把這個(gè)返回值通過Promise.resole()封裝成Promise對(duì)象,要取這個(gè)值也很簡(jiǎn)單,直接通過then()就能取出,如例:

res.then(a => {
  console.log(a) // 2
})

在沒有await的情況下,調(diào)用async函數(shù),會(huì)立即執(zhí)行,返回一個(gè)Promise,那加上await會(huì)有什么不同呢?

await作用

一般情況下,await命令后面接的是一個(gè)Promise對(duì)象,等待Promise對(duì)象狀態(tài)發(fā)生變化,得到返回值,但是也可以接任意表達(dá)式的返回結(jié)果,來看個(gè)例子:

function a () {
  return 'a'
}
async function b () {
  return 'b'
}
const c = await a()
const d = await b()
console.log(c, d) // 'a' 'b'

復(fù)制代碼由例子可以看到await后面不管接的是什么表達(dá)式,都能等待到結(jié)果的返回,當(dāng)?shù)鹊讲皇荘romise對(duì)象時(shí),就將等到的結(jié)果返回,當(dāng)?shù)鹊降氖且粋€(gè)Promise對(duì)象時(shí),會(huì)阻塞后面的代碼,等待Promise對(duì)象狀態(tài)變化,得到對(duì)應(yīng)的值作為await等待的結(jié)果,這里的阻塞指的是async內(nèi)部的阻塞,async函數(shù)的調(diào)用并不會(huì)阻塞

解決了什么問題

Promise...then語法已經(jīng)解決了以前一直存在的多層回調(diào)嵌套的問題,那問什么還要用async/await呢?要解答這個(gè)問題先來看一段Promise代碼:

function login () {
  return new Promise(resolve => {
    resolve('aaaa')
  })
}
function getUserInfo (token) {
  return new Promise(resolve => {
    if (token) {
      resolve({
        isVip: true
      })
    }
  })
}
function getVipGoods (userInfo) {
  return new Promise(resolve => {
    if (userInfo.isVip) {
      resolve({
        id: 'xxx',
        price: 'xxx'
      })
    }
  })
}
function showVipGoods (vipGoods) {
  console.log(vipGoods.id + '----' + vipGoods.price)
}
login()
  .then(token => getUserInfo(token))
  .then(userInfo => getVipGoods(userInfo))
  .then(vipGoods => showVipGoods(vipGoods))

如例子所示,每一個(gè)Promise相當(dāng)于一個(gè)異步的網(wǎng)絡(luò)請(qǐng)求,通常一個(gè)業(yè)務(wù)流程需要多個(gè)網(wǎng)絡(luò)請(qǐng)求,而且網(wǎng)絡(luò)請(qǐng)求網(wǎng)絡(luò)請(qǐng)求都依賴一個(gè)的請(qǐng)求結(jié)果,上例就是Promise模擬了這個(gè)過程,下面我們?cè)賮砜纯从胊sync/await會(huì)有什么不同,如例:

 async function call() {
  const token = await login()
  const userInfo = await getUserInfo(token)
  const vipGoods = await getVipGoods(userInfo)
  showVipGoods(vipGoods)
}
call()

和Promise的then鏈調(diào)用相比,async/await的調(diào)用更加清晰簡(jiǎn)單,和同步代碼一樣

帶來了什么問題

使用async/await我們經(jīng)常會(huì)忽略一個(gè)問題,同步執(zhí)行帶來的時(shí)間累加,會(huì)導(dǎo)致程序變慢,有時(shí)候我們的代碼可以寫成并發(fā)執(zhí)行,但是由于async/await做成了繼發(fā)執(zhí)行,來看一個(gè)例子:

function test () {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('test')
      resolve()
    }, 1000)
  })
}
function test1 () {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('test1')
      resolve()
    }, 1000)
  })
}
function test2 () {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('test2')
      resolve()
    }, 1000)
  })
}
async function call () {
  await test()
  await test1()
  await test2()
}
call ()

上面代碼繼發(fā)執(zhí)行,所花時(shí)間是:


實(shí)際上,這段代碼執(zhí)行順序,我并不關(guān)心,繼發(fā)執(zhí)行就浪費(fèi)大量執(zhí)行時(shí)間,下面改成并發(fā)執(zhí)行:

function call () {
  Promise.all([test(), test1(), test2()])
}
call()

所花時(shí)間:

因此在使用async/await時(shí)需要特別注意這一點(diǎn)

循環(huán)中的小問題

在寫JS循環(huán)時(shí),JS提供了許多好用數(shù)組api接口,forEach就是其中一個(gè),但是碰上了async/await,可能就悲劇了,得到了不是你想要的結(jié)果,來看一個(gè)例子:

function getUserInfo (id) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({
        id: id,
        name: 'xxx',
        age: 'xxx'
      })
    }, 200)
  })
}
const users = [{id: 1}, {id: 2}, {id: 3}]
let userInfos = []
users.forEach(async user => {
  let info = await getUserInfo(user.id)
  userInfos.push(info)
})
console.log(userInfos) // []

上面這段代碼是不是很熟悉,模擬獲取多個(gè)用戶的用戶信息,然后得到一個(gè)用戶信息數(shù)組,但是很遺憾,上面的userInfos得到的是一個(gè)空數(shù)組,上面這段代碼加上了async/await后,forEach循環(huán)就變成了異步的,因此不會(huì)等到所有用戶信息都請(qǐng)求完才打印userInfos,想要等待結(jié)果的返回再打印,還是要回到老式的for循環(huán),來看代碼:

function getUserInfo (id) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({
        id: id,
        name: 'xxx',
        age: 'xxx'
      })
    }, 200)
  })
}
const users = [{id: 1}, {id: 2}, {id: 3}]
let userInfos = []
async function call() {
  for (user of users) {
    let info = await getUserInfo(user.id)
    userInfos.push(info)
  }
  console.log(userInfos)
}
call()

上面這種寫法是繼發(fā)式的,也就是會(huì)等前面一個(gè)任務(wù)執(zhí)行完,再執(zhí)行下一個(gè),但是也許你并不關(guān)心執(zhí)行過程,只要拿到想要的結(jié)果就行了,這時(shí)并發(fā)式的效率會(huì)更高,來看代碼:

function getUserInfo (id) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({
        id: id,
        name: 'xxx',
        age: 'xxx'
      })
    }, 200)
  })
}
const users = [{id: 1}, {id: 2}, {id: 3}]
let userInfos = []
const promises = users.map(user => getUserInfo(user.id))
Promise.all(promises).then(res => {
  userInfos = res
  console.log(userInfos)
})

由上面例子可以看到并發(fā)執(zhí)行的效率要高得多

總結(jié)

此篇文章async/await的用法和經(jīng)常遇到的一些問題做了簡(jiǎn)單的總結(jié),希望能對(duì)大家在使用的時(shí)候有所幫助。如果有錯(cuò)誤或不嚴(yán)謹(jǐn)?shù)牡胤剑瑲g迎批評(píng)指正,如果喜歡,歡迎點(diǎn)贊

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容