• async/await异步操作的使用场景


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

    超时处理

    一个请求发出,我们是无法确定什么时候能返回的,也总不能一直傻等,设置超时处理有时候是很有必要的
    function timeOut(delay){
    return new Promise((resolve,reject)=>{
    setTimeout(()=>{
    reject(new Error('已超时'))
    })
    })
    }
    async function imageCrawler(url,delay) {
    try {
    let img = await Promise.race([getImage(url), timeOut(delay)])
    return img
    } catch (error) {
    console.log(error)
    }
    }

    并发限制

    在并发请求的场景中,如果需要大量并发,必须要进行并发限制,不然会被网站屏蔽或者造成进程奔溃
    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
    }

  • 相关阅读:
    接口和类的关系
    Java9+版本中,Interface的内容
    XSS简介
    上传漏洞(一)
    上传漏洞(二)
    初学Django
    ISCC:Please give me username and password!
    各种密码
    Debian 8.9 搭建wordpress个人博客
    网安相关书籍
  • 原文地址:https://www.cnblogs.com/huayang1995/p/15048159.html
Copyright © 2020-2023  润新知