Callback ——从同步思维切换到异步思维

简介: Callback ——从同步思维切换到异步思维

摄影:产品经理薄如蝉翼

我们平时使用Requests的时候,一般是这样写代码的:

import requests
def parse(html):
    print('对 html 进行处理')
html = requests.get('url')
parse(html)

这是一种非常常见的直线性思维,我先请求网站拿到 html,然后我再把 html 传给负责处理的函数。在整个过程中,“我“担任着调度的角色。

在这种思维方式的影响下,有些同学即使在使用aiohttp写异步爬虫,也是这样写的:

import aiohttp
import asyncio
async def request(url):
    async with aiohttp.ClientSession() as session:
        resp = await session.get(url)
        html = await resp.text(encoding='utf-8')
def parse(html):
    print('处理 html')
async def main():
    url_list = [url1, url2, url3, url4]
    tasks = []
    for url in url_list:
        tasks.append(request(url))
    html_list = await asyncio.gather(*tasks)
    for html in html_list:
        parse(html)
if __name__ == '__main__':
    asyncio.run(main())

确实,这些 URL 的网络请求是异步了,但是却必须等到所有 URL 全部请求完成以后,才能开始处理这些 HTML。假如其中一个 URL 访问只需要1秒钟,其他的 URL 请求需要3秒钟。那么这个1秒钟的请求结束以后,还需要等待2秒,才能开始进行处理。

于是,有些同学会修改代码,多包装一层函数:

import aiohttp
import asyncio
async def request(url):
    async with aiohttp.ClientSession() as session:
        resp = await session.get(url)
        html = await resp.text(encoding='utf-8')
def parse(html):
    print('处理 html')
async def get(url):
    html = await request(url)
    parse(html)
async def main():
    url_list = [url1, url2, url3, url4]
    tasks = []
    for url in url_list:
        tasks.append(get(url))
    await asyncio.gather(*tasks)
if __name__ == '__main__':
    asyncio.run(main())

get()函数整体负责获取一个 URL 的源代码并对它进行解析。然后让 get()函数异步。

这样做确实能够解决问题,但是大家如果仔细体会就会发现,在get()函数里面的代码写法,还是用的同步处理的思想。

既然要写异步代码,那么我们脑子里就要一直记住——很多个请求会同时发出,但是我们并不知道他们什么时候完成。与其让我们去等待它完成,然后再把完成结果传给另外一个函数。不如让这些请求在结束的时候,自行主动把结果传给处理函数。

有了这种思想以后,我们再来修改一下上面的代码:

import aiohttp
import asyncio
async def request(url, callback):
    async with aiohttp.ClientSession() as session:
        resp = await session.get(url)
        html = await resp.text(encoding='utf-8')
    callback(html)
def parse(html):
    print('处理 html: ', html)
async def main():
    url_list = [
 'http://exercise.kingname.info/exercise_middleware_ip/1',
 'http://exercise.kingname.info/exercise_middleware_ip/2',
 'http://exercise.kingname.info/exercise_middleware_ip/3',
 'http://exercise.kingname.info/exercise_middleware_ip/4',
 'http://exercise.kingname.info/exercise_middleware_ip/5',
 'http://exercise.kingname.info/exercise_middleware_ip/6',
 ]
    tasks = []
    for url in url_list:
        tasks.append(request(url, parse))
    await asyncio.gather(*tasks)
if __name__ == '__main__':
    asyncio.run(main())

运行效果如下图所示:

这种写法,初看起来与用get()函数包装没什么区别,但是他们在思维方式上却完全不一样。

这种不一样,接下来的几篇文章会进一步演示。

友情提示,五一高能预警。

目录
相关文章
|
人工智能 自然语言处理 安全
探秘SuperCLUE-Safety:为中文大模型打造的多轮对抗安全新框架
探秘SuperCLUE-Safety:为中文大模型打造的多轮对抗安全新框架【2月更文挑战第2天】
探秘SuperCLUE-Safety:为中文大模型打造的多轮对抗安全新框架
|
6月前
|
人工智能 vr&ar 图形学
谷歌DeepMind联手牛津推出Bolt3D:AI秒速3D建模革命!单GPU仅需6秒生成3D场景
牛津大学与谷歌联合推出的Bolt3D技术,能在单个GPU上仅用6.25秒从单张或多张图像生成高质量3D场景,基于高斯溅射和几何多视角扩散模型,为游戏、VR/AR等领域带来革命性突破。
250 2
谷歌DeepMind联手牛津推出Bolt3D:AI秒速3D建模革命!单GPU仅需6秒生成3D场景
|
Java Spring
Cron表达式介绍
Cron表达式介绍
388 0
|
存储 弹性计算 前端开发
云服务器 ECS产品使用问题之如何从互联网访问ECS实例上的某个文件
云服务器ECS(Elastic Compute Service)是各大云服务商阿里云提供的一种基础云计算服务,它允许用户租用云端计算资源来部署和运行各种应用程序。以下是一个关于如何使用ECS产品的综合指南。
|
开发者
【Qt 学习笔记】Qt系统相关 | Qt事件 | 事件的介绍及基本概念
【Qt 学习笔记】Qt系统相关 | Qt事件 | 事件的介绍及基本概念
505 4
|
并行计算 Ubuntu Linux
openEuler操作系统禁用 Nouveau
openEuler操作系统禁用 Nouveau
645 1
|
Java
详尽分享线程池的4种拒绝策略
详尽分享线程池的4种拒绝策略
695 0
|
安全 IDE Java
Spring高手之路10——解锁Spring组件扫描的新视角
这篇文章详细讲解了Spring包扫描的各种特性和策略,包括基础的包扫描路径设置,按注解过滤,正则表达式过滤,Assignable类型过滤,以及自定义过滤等多种过滤策略。同时,文章还深入分析了Spring是如何生成默认bean名称的源码,并介绍了Java的内省机制在生成默认bean名称中的应用。这篇文章为你成为Spring高手打下坚实的基础。
407 1
Spring高手之路10——解锁Spring组件扫描的新视角
|
关系型数据库 MySQL
MySql插入唯一键冲突的三种可选方式
MySql插入一条记录,结果提示主键冲突,怎么办? 批量插入数据时,发现插入的这批数据中,有某些记录存在唯一键冲突,一个一个跳出来就比较麻烦了,有什么好的办法直接忽略掉冲突的记录么? 下面简单记录三种处理方式
901 0
MySql插入唯一键冲突的三种可选方式
|
存储 运维 监控
Apache Dubbo 云原生可观测性的探索与实践
Apache Dubbo 已接入指标、链路、日志等多维度观测能力,助力云原生实践,本文将介绍 Dubbo 可观测性的探索与实践。
Apache Dubbo 云原生可观测性的探索与实践