小編給大家分享一下async/await異步應(yīng)用的常用場景有哪些,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

浦口ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為成都創(chuàng)新互聯(lián)公司的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18980820575(備注:SSL證書合作)期待與您的合作!
前言
async/await 語法用看起來像寫同步代碼的方式來優(yōu)雅地處理異步操作,但是我們也要明白一點,異步操作本來帶有復雜性,像寫同步代碼的方式并不能降低本質(zhì)上的復雜性,所以在處理上我們要更加謹慎, 稍有不慎就可能寫出不是預(yù)期執(zhí)行的代碼,從而影響執(zhí)行效率。
最普遍的異步操作就是請求,我們也可以用 setTimeOut 來簡單模擬異步請求。
場景1. 一個請求接著一個請求
相信這個場景是最常遇到,后一個請求依賴前一個請求,下面以爬取一個網(wǎng)頁內(nèi)的圖片為例子進行描述,使用了 superagent 請求模塊, cheerio 頁面分析模塊,圖片的地址需要分析網(wǎng)頁內(nèi)容得出,所以必須按順序進行請求。
const request = require('superagent')
const cheerio = require('cheerio')
// 簡單封裝下請求,其他的類似
function getHTML(url) {
// 一些操作,比如設(shè)置一下請求頭信息
return superagent.get(url).set('referer', referer).set('user-agent', userAgent)
}
// 下面就請求一張圖片
async function imageCrawler(url) {
let res = await getHTML(url)
let html = res.text
let $ = cheerio.load(html)
let $img = $(selector)[0]
let href = $img.attribs.src
res = await getImage(href)
retrun res.body
}
async function handler(url) {
let img = await imageCrawler(url)
console.log(img) // buffer 格式的數(shù)據(jù)
// 處理圖片
}
handler(url)上面就是一個簡單的獲取圖片數(shù)據(jù)的場景,圖片數(shù)據(jù)是加載進內(nèi)存中,如果只是簡單的存儲數(shù)據(jù),可以用流的形式進行存儲,以防止消耗太多內(nèi)存。
其中 await getHTML 是必須的,如果省略了 await 程序就不能按預(yù)期得到結(jié)果。執(zhí)行流程會先執(zhí)行 await 后面的表達式,其實際返回的是一個處于 pending 狀態(tài)的 promise,等到這個 promise 處于已決議狀態(tài)后才會執(zhí)行 await 后面的操作,其中的代碼執(zhí)行會跳出 async 函數(shù),繼續(xù)執(zhí)行函數(shù)外面的其他代碼,所以并不會阻塞后續(xù)代碼的執(zhí)行。
場景2.并發(fā)請求
有的時候我們并不需要等待一個請求回來才發(fā)出另一個請求,這樣效率是很低的,所以這個時候就需要并發(fā)執(zhí)行請求任務(wù)。下面以一個查詢?yōu)槔?先獲取一個人的學校地址和家庭住址,再由這些信息獲取詳細的個人信息,學校地址和家庭住址是沒有依賴關(guān)系的,后面的獲取個人信息依賴于兩者
async function infoCrawler(url, name) {
let [schoolAdr, homeAdr] = await Promise.all([getSchoolAdr(name), getHomeAdr(name)])
let info = await getInfo(url + `?schoolAdr=${schoolAdr}&homeAdr=${homeAdr}`)
return info
}上面使用的 Promise.all 里面的異步請求都會并發(fā)執(zhí)行,并等到數(shù)據(jù)都準備后返回相應(yīng)的按數(shù)據(jù)順序返回的數(shù)組,這里最后處理獲取信息的時間,由并發(fā)請求中最慢的請求決定,例如 getSchoolAdr 遲遲不返回數(shù)據(jù),那么后續(xù)操作只能等待,就算 getHomeAdr 已經(jīng)提前返回了,當然以上場景必須是這么做,但是有的時候我們并不需要這么做。
上面第一個場景中,我們只獲取到一張圖片,但是可能一個網(wǎng)頁中不止一張圖片,如果我們要把這些圖片存儲起來,其實是沒有必要等待圖片都并發(fā)請求回來后再處理,哪張圖片早回來就存儲哪張就行了
let imageUrls = ['href1', 'href2', 'href3']
async function saveImages(imageUrls) {
await Promise.all(imageUrls.map(async imageUrl => {
let img = await getImage(imageUrl)
return await saveImage(img)
}))
console.log('done')
}// 如果我們連存儲是否全部完成也不關(guān)心,也可以這么寫
let imageUrls = ['href1', 'href2', 'href3']
// saveImages() 連 async 都省了
function saveImages(imageUrls) {
imageUrls.forEach(async imageUrl => {
let img = await getImage(imageUrl)
saveImage(img)
})
}可能有人會疑問 forEach 不是不能用于異步嗎,這個說法我也在剛接觸這個語法的時候就聽說過,很明顯 forEach 是可以處理異步的,只是是并發(fā)處理,map 也是并發(fā)處理,這個怎么用主要看你的實際場景,還要看你是否對結(jié)果感興趣
場景3.錯誤處理
一個請求發(fā)出,可以會遇到各種問題,我們是無法保證一定成功的,報錯是常有的事,所以處理錯誤有時很有必要, async/await 處理錯誤也非常直觀, 使用 try/catch 直接捕獲就可以了
async function imageCrawler(url) {
try {
let img = await getImage(url)
return img
} catch (error) {
console.log(error)
}
}// imageCrawler 返回的是一個 promise 可以這樣處理
async function imageCrawler(url) {
let img = await getImage(url)
return img
}
imageCrawler(url).catch(err => {
console.log(err)
})可能有人會有疑問,是不是要在每個請求中都 try/catch 一下,這個其實你在最外層 catch 一下就可以了,一些基于中間件的設(shè)計就喜歡在最外層捕獲錯誤
async function ctx(next) {
try {
await next()
} catch (error) {
console.log(error)
}
}場景4. 超時處理
一個請求發(fā)出,我們是無法確定什么時候返回的,也總不能一直傻傻的等,設(shè)置超時處理有時是很有必要的
function timeOut(delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('不用等了,別傻了'))
}, delay)
})
}
async function imageCrawler(url,delay) {
try {
let img = await Promise.race([getImage(url), timeOut(delay)])
return img
} catch (error) {
console.log(error)
}
}這里使用 Promise.race 處理超時,要注意的是,如果超時了,請求還是沒有終止的,只是不再進行后續(xù)處理。當然也不用擔心,后續(xù)處理會報錯而導致重新處理出錯信息, 因為 promise 的狀態(tài)一經(jīng)改變是不會再改變的
場景5. 并發(fā)限制
在并發(fā)請求的場景中,如果需要大量并發(fā),必須要進行并發(fā)限制,不然會被網(wǎng)站屏蔽或者造成進程崩潰
async function getImages(urls, limit) {
let running = 0
let r
let p = new Promise((resolve, reject) => {
r = resolve
})
function run() {
if (running < limit && urls.length > 0) {
running++
let url = urls.shift();
(async () => {
let img = await getImage(url)
running--
console.log(img)
if (urls.length === 0 && running === 0) {
console.log('done')
return r('done')
} else {
run()
}
})()
run() // 立即到并發(fā)上限
}
}
run()
return await p
}總結(jié)
以上列舉了一些日常場景處理的代碼片段,在遇到比較復雜場景時,可以結(jié)合以上的場景進行組合使用,如果場景過于復雜,最好的辦法是使用相關(guān)的異步代碼控制庫。如果想更好地了解 async/await 可以先去了解 promise 和 generator, async/await 基本上是 generator 函數(shù)的語法糖,下面簡單的描述了一下內(nèi)部的原理。
function delay(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(time)
}, time)
})
}
function *createTime() {
let time1 = yield delay(1000)
let time2 = yield delay(2000)
let time3 = yield delay(3000)
console.log(time1, time2, time3)
}
let iterator = createTime()
console.log(iterator.next())
console.log(iterator.next(1000))
console.log(iterator.next(2000))
console.log(iterator.next(3000))
// 輸出
{ value: Promise { <pending> }, done: false }
{ value: Promise { <pending> }, done: false }
{ value: Promise { <pending> }, done: false }
1000 2000 3000
{ value: undefined, done: true }可以看出每個 value 都是 Promise,并且通過手動傳入?yún)?shù)到 next 就可以設(shè)置生成器內(nèi)部的值,這里是手動傳入,我只要寫一個遞歸函數(shù)讓其自動添進去就可以了
function run(createTime) {
let iterator = createTime()
let result = iterator.next()
function autoRun() {
if (!result.done) {
Promise.resolve(result.value).then(time => {
result = iterator.next(time)
autoRun()
}).catch(err => {
result = iterator.throw(err)
autoRun()
})
}
}
autoRun()
}
run(createTime)promise.resove 保證返回的是一個 promise 對象 可迭代對象除了有 next 方法還有 throw 方法用于往生成器內(nèi)部傳入錯誤,只要生成內(nèi)部能捕獲該對象,生成器就可以繼承運行,類似下面的代碼
function delay(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (time == 2000) {
reject('2000錯誤')
}
resolve(time)
}, time)
})
}
function *createTime() {
let time1 = yield delay(1000)
let time2
try {
time2 = yield delay(2000)
} catch (error) {
time2 = error
}
let time3 = yield delay(3000)
console.log(time1, time2, time3)
}可以看出生成器函數(shù)其實和 async/await 語法長得很像,只要改一下 async/await 代碼片段就是生成器函數(shù)了
async function createTime() {
let time1 = await delay(1000)
let time2
try {
time2 = await delay(2000)
} catch (error) {
time2 = error
}
let time3 = await delay(3000)
console.log(time1, time2, time3)
}
function transform(async) {
let str = async.toString()
str = str.replace(/async\s+(function)\s+/, '$1 *').replace(/await/g, 'yield')
return str
}以上是“async/await異步應(yīng)用的常用場景有哪些”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
當前標題:async/await異步應(yīng)用的常用場景有哪些
網(wǎng)站地址:http://chinadenli.net/article46/jijshg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)建站、做網(wǎng)站、自適應(yīng)網(wǎng)站、App開發(fā)、網(wǎng)站排名、云服務(wù)器
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)