亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

詳解async/await 異步應用的常用場景

2019-11-19 11:35:32
字體:
來源:轉載
供稿:網友

前言

async/await 語法用看起來像寫同步代碼的方式來優雅地處理異步操作,但是我們也要明白一點,異步操作本來帶有復雜性,像寫同步代碼的方式并不能降低本質上的復雜性,所以在處理上我們要更加謹慎, 稍有不慎就可能寫出不是預期執行的代碼,從而影響執行效率。下面將簡單地描述一下一些日常常用場景,加深對 async/await 認識
最普遍的異步操作就是請求,我們也可以用 setTimeOut 來簡單模擬異步請求。

場景1. 一個請求接著一個請求

相信這個場景是最常遇到,后一個請求依賴前一個請求,下面以爬取一個網頁內的圖片為例子進行描述,使用了 superagent 請求模塊, cheerio 頁面分析模塊,圖片的地址需要分析網頁內容得出,所以必須按順序進行請求。

const request = require('superagent')const cheerio = require('cheerio')// 簡單封裝下請求,其他的類似function getHTML(url) {// 一些操作,比如設置一下請求頭信息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 格式的數據  // 處理圖片}handler(url)

上面就是一個簡單的獲取圖片數據的場景,圖片數據是加載進內存中,如果只是簡單的存儲數據,可以用流的形式進行存儲,以防止消耗太多內存。

其中 await getHTML 是必須的,如果省略了 await 程序就不能按預期得到結果。執行流程會先執行 await 后面的表達式,其實際返回的是一個處于 pending 狀態的 promise,等到這個 promise 處于已決議狀態后才會執行 await 后面的操作,其中的代碼執行會跳出 async 函數,繼續執行函數外面的其他代碼,所以并不會阻塞后續代碼的執行。

場景2.并發請求

有的時候我們并不需要等待一個請求回來才發出另一個請求,這樣效率是很低的,所以這個時候就需要并發執行請求任務。下面以一個查詢為例,先獲取一個人的學校地址和家庭住址,再由這些信息獲取詳細的個人信息,學校地址和家庭住址是沒有依賴關系的,后面的獲取個人信息依賴于兩者

 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 里面的異步請求都會并發執行,并等到數據都準備后返回相應的按數據順序返回的數組,這里最后處理獲取信息的時間,由并發請求中最慢的請求決定,例如 getSchoolAdr 遲遲不返回數據,那么后續操作只能等待,就算 getHomeAdr 已經提前返回了,當然以上場景必須是這么做,但是有的時候我們并不需要這么做。

上面第一個場景中,我們只獲取到一張圖片,但是可能一個網頁中不止一張圖片,如果我們要把這些圖片存儲起來,其實是沒有必要等待圖片都并發請求回來后再處理,哪張圖片早回來就存儲哪張就行了

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')}
// 如果我們連存儲是否全部完成也不關心,也可以這么寫let imageUrls = ['href1', 'href2', 'href3']// saveImages() 連 async 都省了function saveImages(imageUrls) {  imageUrls.forEach(async imageUrl => {  let img = await getImage(imageUrl)  saveImage(img)  })}

可能有人會疑問 forEach 不是不能用于異步嗎,這個說法我也在剛接觸這個語法的時候就聽說過,很明顯 forEach 是可以處理異步的,只是是并發處理,map 也是并發處理,這個怎么用主要看你的實際場景,還要看你是否對結果感興趣

場景3.錯誤處理

一個請求發出,可以會遇到各種問題,我們是無法保證一定成功的,報錯是常有的事,所以處理錯誤有時很有必要, 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 一下就可以了,一些基于中間件的設計就喜歡在最外層捕獲錯誤

async function ctx(next) {  try {    await next()  } catch (error) {    console.log(error)  }}

場景4. 超時處理

一個請求發出,我們是無法確定什么時候返回的,也總不能一直傻傻的等,設置超時處理有時是很有必要的

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 處理超時,要注意的是,如果超時了,請求還是沒有終止的,只是不再進行后續處理。當然也不用擔心,后續處理會報錯而導致重新處理出錯信息, 因為 promise 的狀態一經改變是不會再改變的

場景5. 并發限制

在并發請求的場景中,如果需要大量并發,必須要進行并發限制,不然會被網站屏蔽或者造成進程崩潰

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() // 立即到并發上限    }  }  run()  return await p}

總結

以上列舉了一些日常場景處理的代碼片段,在遇到比較復雜場景時,可以結合以上的場景進行組合使用,如果場景過于復雜,最好的辦法是使用相關的異步代碼控制庫。如果想更好地了解 async/await 可以先去了解 promise 和 generator, async/await 基本上是 generator 函數的語法糖,下面簡單的描述了一下內部的原理。

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,并且通過手動傳入參數到 next 就可以設置生成器內部的值,這里是手動傳入,我只要寫一個遞歸函數讓其自動添進去就可以了

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 方法用于往生成器內部傳入錯誤,只要生成內部能捕獲該對象,生成器就可以繼承運行,類似下面的代碼

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)}

可以看出生成器函數其實和 async/await 語法長得很像,只要改一下 async/await 代碼片段就是生成器函數了

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}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
午夜精品福利电影| 一区二区成人精品| 国产精品69久久久久| 成人免费看吃奶视频网站| 最近2019中文字幕第三页视频| 欧美性xxxx极品高清hd直播| 亚洲另类图片色| 亚洲自拍在线观看| 欧美另类极品videosbest最新版本| 欧美国产日韩一区二区在线观看| 亚洲电影在线观看| 欧美激情一区二区三区在线视频观看| 日本一欧美一欧美一亚洲视频| 国产精品美乳在线观看| 亚洲欧洲av一区二区| 国产一区二区三区四区福利| 久久久久日韩精品久久久男男| 亚洲国产另类 国产精品国产免费| 欧洲午夜精品久久久| 国产日韩换脸av一区在线观看| 日韩欧美亚洲综合| 日韩中文有码在线视频| 国产日韩欧美夫妻视频在线观看| 亚洲欧洲午夜一线一品| 国产精品久久久久久久app| 精品久久香蕉国产线看观看gif| 亚洲成人激情在线| 欧美成人精品一区| 中文字幕亚洲专区| 精品高清一区二区三区| 国产精品高潮呻吟视频| 欧美一区二区视频97| www亚洲精品| 精品免费在线视频| 亚洲人成网站777色婷婷| 国内揄拍国内精品| 欧美电影免费观看高清完整| 国产精品天天狠天天看| 国产精品爱啪在线线免费观看| 91在线免费看网站| 欧美大片网站在线观看| 2024亚洲男人天堂| 日韩成人在线视频观看| 欧美国产欧美亚洲国产日韩mv天天看完整| 欧美精品一区二区三区国产精品| 欧美高清视频一区二区| 国产精品视频导航| 国产精品人成电影在线观看| 日韩电影在线观看免费| 成人亚洲综合色就1024| 4438全国亚洲精品在线观看视频| 久久精视频免费在线久久完整在线看| 国产精品狼人色视频一区| 4438全国成人免费| 色琪琪综合男人的天堂aⅴ视频| 91久久国产精品91久久性色| 国产在线播放不卡| 最近2019年日本中文免费字幕| 欧美韩国理论所午夜片917电影| 91国偷自产一区二区三区的观看方式| 91最新国产视频| 国产成人久久精品| 欧美精品一区二区三区国产精品| 亚洲韩国青草视频| 日韩欧美国产免费播放| 亚洲精品v天堂中文字幕| 国产亚洲精品日韩| 成人精品视频99在线观看免费| 亚洲精品欧美一区二区三区| 国产日产欧美精品| 国产精品美女午夜av| 久久精品视频网站| 国产精品99久久久久久久久| 国产成人精品国内自产拍免费看| 上原亚衣av一区二区三区| 成人欧美一区二区三区在线湿哒哒| 精品欧美国产一区二区三区| 在线视频日本亚洲性| 国产成人精品电影| 久久国产精品免费视频| 亚洲欧美国产一区二区三区| 亚洲精品国产精品国产自| 亚洲一级一级97网| 日韩在线观看网址| 久久久噜噜噜久久| 欧美日韩亚洲激情| 色婷婷av一区二区三区在线观看| 欧美激情综合色综合啪啪五月| 欧洲美女7788成人免费视频| 日本中文字幕不卡免费| 亚洲精品久久久久久久久久久久| 欧美极品欧美精品欧美视频| 91综合免费在线| 91精品国产91久久久久久最新| www.日韩欧美| 日av在线播放中文不卡| 欧美中在线观看| 欧美中文在线观看| 日韩精品在线免费观看| 亚洲伊人一本大道中文字幕| 成人在线视频网站| 国内精品视频一区| 精品久久久国产精品999| 欧美成人一区二区三区电影| 亚洲电影天堂av| 一本色道久久88精品综合| 国产欧美精品日韩| 久久久999精品视频| 亚洲网址你懂得| 欧美日韩中文在线| 国产精品久久久久久一区二区| 亚洲欧美综合区自拍另类| 中文字幕少妇一区二区三区| 最近2019好看的中文字幕免费| 国产精品成人播放| 777国产偷窥盗摄精品视频| 亚洲日本中文字幕免费在线不卡| 欧美丝袜一区二区| 久久精品国产99国产精品澳门| 日韩电影在线观看中文字幕| 性欧美xxxx视频在线观看| 奇米影视亚洲狠狠色| 亚洲欧美另类在线观看| 亚洲激情在线观看视频免费| 欧美尺度大的性做爰视频| 国产成人激情视频| 一本一道久久a久久精品逆3p| 国产精品视频最多的网站| 91精品一区二区| 亚洲一品av免费观看| 亚洲精品日韩欧美| 国产主播精品在线| 2019国产精品自在线拍国产不卡| 国产精品第10页| 亚洲国产精品推荐| 日韩在线一区二区三区免费视频| 亚洲国产小视频在线观看| 亚洲国产美女精品久久久久∴| 2019中文字幕全在线观看| 成人免费视频a| 欧美一区三区三区高中清蜜桃| 亚洲精品资源在线| 色天天综合狠狠色| 成人在线视频网站| 欧美激情亚洲精品| 精品亚洲一区二区| 91久久国产精品| 亚洲一区二区三区xxx视频| 精品国产一区二区三区久久狼5月| 91高清视频在线免费观看| 一区二区三区回区在观看免费视频| 亚洲天堂一区二区三区| 成人动漫网站在线观看| 国产女精品视频网站免费| 一区二区三区无码高清视频| 亚洲影视九九影院在线观看| 亚洲精品wwww| 久久亚洲春色中文字幕| 亚洲人成五月天| 懂色av影视一区二区三区| 久久久久成人网| 欧美高清在线播放| 国产高清视频一区三区| 日韩av影视在线|