真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

js中async與Promise的關(guān)系是什么-創(chuàng)新互聯(lián)

小編給大家分享一下js中async與Promise的關(guān)系是什么,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

創(chuàng)新互聯(lián)公司是一家專(zhuān)業(yè)提供立山企業(yè)網(wǎng)站建設(shè),專(zhuān)注與成都網(wǎng)站制作、成都網(wǎng)站建設(shè)、外貿(mào)營(yíng)銷(xiāo)網(wǎng)站建設(shè)成都h5網(wǎng)站建設(shè)、小程序制作等業(yè)務(wù)。10年已為立山眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專(zhuān)業(yè)網(wǎng)站建設(shè)公司優(yōu)惠進(jìn)行中。

2018年已經(jīng)到了5月份,node的4.x版本也已經(jīng)停止了維護(hù)我司的某個(gè)服務(wù)也已經(jīng)切到了8.x,目前正在做koa2.x的遷移將之前的generator全部替換為async但是,在替換的過(guò)程中,發(fā)現(xiàn)一些濫用async導(dǎo)致的時(shí)間上的浪費(fèi) 所以來(lái)談一下,如何優(yōu)化async代碼,更充分的利用異步事件流 杜絕濫用async

首先,你需要了解Promise

Promise是使用async/await的基礎(chǔ),所以你一定要先了解Promise是做什么的

Promise是幫助解決回調(diào)地獄的一個(gè)好東西,能夠讓異步流程變得更清晰。

 一個(gè)簡(jiǎn)單的Error-first-callback轉(zhuǎn)換為Promise的例子:

const fs = require('fs')
function readFile (fileName) {
 return new Promise((resolve, reject) => {
  fs.readFile(fileName, (err, data) => {
   if (err) reject(err)

   resolve(data)
  })
 })
}
readFile('test.log').then(data => {
 console.log('get data')
}, err => {
 console.error(err)
})

我們調(diào)用函數(shù)返回一個(gè)Promise的實(shí)例,在實(shí)例化的過(guò)程中進(jìn)行文件的讀取,當(dāng)文件讀取的回調(diào)觸發(fā)式,進(jìn)行Promise狀態(tài)的變更,resolved或者rejected狀態(tài)的變更我們使用then來(lái)監(jiān)聽(tīng),第一個(gè)回調(diào)為resolve的處理,第二個(gè)回調(diào)為reject的處理。

async與Promise的關(guān)系

async函數(shù)相當(dāng)于一個(gè)簡(jiǎn)寫(xiě)的返回Promise實(shí)例的函數(shù),效果如下:

function getNumber () {
 return new Promise((resolve, reject) => {
  resolve(1)
 })
}
// =>
async function getNumber () {
 return 1
}

兩者在使用上方式上完全一樣,都可以在調(diào)用getNumber函數(shù)后使用then進(jìn)行監(jiān)聽(tīng)返回值。 以及與async對(duì)應(yīng)的await語(yǔ)法的使用方式:

getNumber().then(data => {
 // got data
})
// =>
let data = await getNumber()

await的執(zhí)行會(huì)獲取表達(dá)式后邊的Promise執(zhí)行結(jié)果,相當(dāng)于我們調(diào)用then獲取回調(diào)結(jié)果一樣。 P.S. 在async/await支持度還不是很高的時(shí)候,大家都會(huì)選擇使用generator/yield結(jié)合著一些類(lèi)似于co的庫(kù)來(lái)實(shí)現(xiàn)類(lèi)似的效果

async函數(shù)代碼執(zhí)行是同步的,結(jié)果返回是異步的

async函數(shù)總是會(huì)返回一個(gè)Promise的實(shí)例 這點(diǎn)兒很重要所以說(shuō)調(diào)用一個(gè)async函數(shù)時(shí),可以理解為里邊的代碼都是處于new Promise中,所以是同步執(zhí)行的而最后return的操作,則相當(dāng)于在Promise中調(diào)用resolve:

async function getNumber () {
 console.log('call getNumber()')

 return 1
}
getNumber().then(_ => console.log('resolved'))
console.log('done')
// 輸出順序:
// call getNumber()
// done
// resolved

Promise內(nèi)部的Promise會(huì)被消化

也就是說(shuō),如果我們有如下的代碼:

function getNumber () {
 return new Promise(resolve => {
  resolve(Promise.resolve(1))
 })
}
getNumber().then(data => console.log(data)) // 1

如果按照上邊說(shuō)的話(huà),我們?cè)趖hen里邊獲取到的data應(yīng)該是傳入resolve中的值 ,也就是另一個(gè)Promise的實(shí)例。
 但實(shí)際上,我們會(huì)直接獲得返回值:1,也就是說(shuō),如果在Promise中返回一個(gè)Promise,實(shí)際上程序會(huì)幫我們執(zhí)行這個(gè)Promise,并在內(nèi)部的Promise狀態(tài)改變時(shí)觸發(fā)then之類(lèi)的回調(diào)。
 一個(gè)有意思的事情:

function getNumber () {
 return new Promise(resolve => {
  resolve(Promise.reject(new Error('Test')))
 })
}
getNumber().catch(err => console.error(err)) // Error: Test

如果我們?cè)趓esolve中傳入了一個(gè)reject,則我們?cè)谕獠縿t可以直接使用catch監(jiān)聽(tīng)到。
這種方式經(jīng)常用于在async函數(shù)中拋出異常 如何在async函數(shù)中拋出異常:

async function getNumber () {
 return Promise.reject(new Error('Test'))
}
try {
 let number = await getNumber()
} catch (e) {
 console.error(e)
}

一定不要忘了await關(guān)鍵字

如果忘記添加await關(guān)鍵字,代碼層面并不會(huì)報(bào)錯(cuò),但是我們接收到的返回值卻是一個(gè)Promise

let number = getNumber()
console.log(number) // Promise

所以在使用時(shí)一定要切記await關(guān)鍵字

let number = await getNumber()
console.log(number) // 1

不是所有的地方都需要添加await

在代碼的執(zhí)行過(guò)程中,有時(shí)候,并不是所有的異步都要添加await的。 比如下邊的對(duì)文件的操作:

我們假設(shè)fs所有的API都被我們轉(zhuǎn)換為了Promise版本

let number = await getNumber()
console.log(number) // 1

我們通過(guò)await打開(kāi)一個(gè)文件,然后進(jìn)行兩次文件的寫(xiě)入。

但是注意了,在兩次文件的寫(xiě)入操作前邊,我們并沒(méi)有添加await關(guān)鍵字。
 因?yàn)檫@是多余的,我們只需要通知API,我要往這個(gè)文件里邊寫(xiě)入一行文本,順序自然會(huì)由fs來(lái)控制
 然后我們?cè)谧詈笫褂胊wait來(lái)關(guān)閉這個(gè)文件。
 因?yàn)槿绻覀兩线呍趫?zhí)行寫(xiě)入的過(guò)程還沒(méi)有完成時(shí),close的回調(diào)是不會(huì)觸發(fā)的,
 也就是說(shuō),回調(diào)的觸發(fā)就意味著上邊兩步的write已經(jīng)執(zhí)行完成了。

合并多個(gè)不相干的async函數(shù)調(diào)用

如果我們現(xiàn)在要獲取一個(gè)用戶(hù)的頭像和用戶(hù)的詳細(xì)信息(而這是兩個(gè)接口 雖說(shuō)一般情況下不太會(huì)出現(xiàn))

async function getUser () {
 let avatar = await getAvatar()
 let userInfo = await getUserInfo()

 return {
  avatar,
  userInfo
 }
}

這樣的代碼就造成了一個(gè)問(wèn)題,我們獲取用戶(hù)信息的接口并不依賴(lài)于頭像接口的返回值。
 但是這樣的代碼卻會(huì)在獲取到頭像以后才會(huì)去發(fā)送獲取用戶(hù)信息的請(qǐng)求。
 所以我們對(duì)這種代碼可以這樣處理:

async function getUser () {
 let [avatar, userInfo] = await Promise.all([getAvatar(), getUserInfo()])

 return {
  avatar,
  userInfo
 }
}

這樣的修改就會(huì)讓getAvatar與getUserInfo內(nèi)部的代碼同時(shí)執(zhí)行,同時(shí)發(fā)送兩個(gè)請(qǐng)求,在外層通過(guò)包一層Promise.all來(lái)確保兩者都返回結(jié)果。

讓相互沒(méi)有依賴(lài)關(guān)系的異步函數(shù)同時(shí)執(zhí)行

一些循環(huán)中的注意事項(xiàng)

forEach

當(dāng)我們調(diào)用這樣的代碼時(shí):

async function getUsersInfo () {
 [1, 2, 3].forEach(async uid => {
  console.log(await getUserInfo(uid))
 })
}
function getuserInfo (uid) {
 return new Promise(resolve => {
  setTimeout(_ => resolve(uid), 1000)
 })
}
await getUsersInfo()

這樣的執(zhí)行好像并沒(méi)有什么問(wèn)題,我們也會(huì)得到1、2、3三條log的輸出,但是當(dāng)我們?cè)赼wait getUsersInfo()下邊再添加一條console.log('done')的話(huà),就會(huì)發(fā)現(xiàn):

 我們會(huì)先得到done,然后才是三條uid的log,也就是說(shuō),getUsersInfo返回結(jié)果時(shí),其實(shí)內(nèi)部Promise并沒(méi)有執(zhí)行完。
 這是因?yàn)閒orEach并不會(huì)關(guān)心回調(diào)函數(shù)的返回值是什么,它只是運(yùn)行回調(diào)。

不要在普通的for、while循環(huán)中使用await

使用普通的for、while循環(huán)會(huì)導(dǎo)致程序變?yōu)榇校?/p>

for (let uid of [1, 2, 3]) {
 let result = await getUserInfo(uid)
}

這樣的代碼運(yùn)行,會(huì)在拿到uid: 1的數(shù)據(jù)后才會(huì)去請(qǐng)求uid: 2的數(shù)據(jù)

--------------------------------------------------------------------------------

關(guān)于這兩種問(wèn)題的解決方案:

目前最優(yōu)的就是將其替換為map結(jié)合著Promise.all來(lái)實(shí)現(xiàn):

await Promise.all([1, 2, 3].map(async uid => await getUserInfo(uid)))

這樣的代碼實(shí)現(xiàn)會(huì)同時(shí)實(shí)例化三個(gè)Promise,并請(qǐng)求getUserInfo

P.S. 草案中有一個(gè)await*,可以省去Promise.all

await Promise.all([1, 2, 3].map(async uid => await getUserInfo(uid)))

P.S. 為什么在使用Generator+co時(shí)沒(méi)有這個(gè)問(wèn)題

在使用koa1.x的時(shí)候,我們直接寫(xiě)yield [].map是不會(huì)出現(xiàn)上述所說(shuō)的串行問(wèn)題的看過(guò)co源碼的小伙伴應(yīng)該都明白,里邊有這么兩個(gè)函數(shù)(刪除了其余不相關(guān)的代碼):

function toPromise(obj) {
 if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
 return obj;
}
function arrayToPromise(obj) {
 return Promise.all(obj.map(toPromise, this));
}

co是幫助我們添加了Promise.all的處理的(膜拜TJ大佬)。

總結(jié)

總結(jié)一下關(guān)于async函數(shù)編寫(xiě)的幾個(gè)小提示:

1.使用return Promise.reject()在async函數(shù)中拋出異常
2.讓相互之間沒(méi)有依賴(lài)關(guān)系的異步函數(shù)同時(shí)執(zhí)行
3.不要在循環(huán)的回調(diào)中/for、while循環(huán)中使用await,用map來(lái)代替它

以上是“js中async與Promise的關(guān)系是什么”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)成都網(wǎng)站設(shè)計(jì)公司行業(yè)資訊頻道!

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線(xiàn),公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性?xún)r(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿(mǎn)足用戶(hù)豐富、多元化的應(yīng)用場(chǎng)景需求。


本文名稱(chēng):js中async與Promise的關(guān)系是什么-創(chuàng)新互聯(lián)
鏈接分享:http://weahome.cn/article/ccgegj.html

其他資訊

在線(xiàn)咨詢(xún)

微信咨詢(xún)

電話(huà)咨詢(xún)

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部