在aiohttp中何时使用await?代码多次用await是否合理?
关于asyncio/aiohttp中多次使用await的疑问解答
为什么需要两次await?这两次await的作用分别是什么?
这两次await作用完全不同,都是异步编程中必要的操作:
await resp.json():resp.json()是异步方法,它需要从网络连接中读取响应体并解析成JSON,这个过程属于IO密集型操作,必须用await等待它完成,同时让出事件循环,让其他异步任务可以继续执行。如果不用await,你拿到的只是一个coroutine对象,不是实际的JSON数据。await asyncio.gather(*tasks):asyncio.create_task是把协程包装成后台运行的任务,调用后任务就开始执行了,但你需要用await asyncio.gather来等待所有任务完成,并收集它们的返回结果。如果不用这个await,函数会直接返回空列表,因为任务还没执行完毕。
这样做是否正确?会不会导致两次阻塞?
完全正确,而且不会造成两次同步阻塞。异步编程中的await不是同步阻塞,它是告诉事件循环:“我现在要等一个IO操作完成,你先去处理其他任务吧”。
- 当每个任务内部执行到
await resp.json()时,事件循环会切换到其他已创建的任务去发送请求或处理响应,不会卡在单个任务上。 - 最后
await asyncio.gather只是等待所有任务都完成,这个过程中事件循环依然在处理其他任务(如果有的话),直到所有任务都返回结果。
代码优化建议(更高效+符合Python风格)
先指出你代码里的几个小问题:
rungo函数里调用的make_request应该是你定义的post函数,参数里的feature应该是mydata,属于笔误。- 返回
list(responses)多余,因为asyncio.gather本身就返回列表。 - 空的
headers={}可以省略,aiohttp会自动添加必要的请求头。
优化后的代码:
from typing import Dict, List, Any import asyncio import aiohttp async def post(client: aiohttp.ClientSession, url: str, mydata: Dict[str, Any]) -> Dict[str, Any]: async with client.post(url=url, json=mydata) as resp: # 检查响应状态码,避免解析失败 resp.raise_for_status() r = await resp.json() return {mydata['name']: r} async def rungo(url: str, mydatas: List[Dict[str, Any]]) -> List[Dict[str, Any]]: async with aiohttp.ClientSession() as session: # 用列表推导式创建任务,更简洁 tasks = [asyncio.create_task(post(client=session, url=url, mydata=data)) for data in mydatas] # gather直接返回列表,无需再转换 responses = await asyncio.gather(*tasks, return_exceptions=False) return responses
额外优化点:
- 添加
resp.raise_for_status(),如果请求返回错误状态码(比如4xx/5xx),会抛出异常,方便排查问题。 return_exceptions=False是默认值,显式写出可以明确:如果任务抛出异常,gather会直接把异常抛出,而不是返回异常对象,可根据需求调整。
多次使用await是常见做法吗?
当然是。异步编程的核心就是用await等待每个异步操作完成,只要你不是串行await单个任务(比如在循环里直接await post(...)而不创建任务),就不会影响效率。像你现在的写法,先批量创建所有任务让它们并行执行,最后用gather等待结果,是异步IO中处理批量请求的标准高效写法。
内容的提问来源于stack exchange,提问作者dpcllalala
相关产品推荐
相关产品推荐

