Aiohttp中async with session.get为何阻塞?如何实现异步请求?
问题1解答
你猜的没错,async with session.get(...) as r确实会等待响应头部返回,这是aiohttp的设计逻辑,不是你对async的理解有误。
aiohttp的session.get()返回ClientResponse对象,当你用async with进入该对象的上下文时,__aenter__方法内部会自动等待服务器返回响应状态行和HTTP头部(也就是等请求进入"响应头就绪"状态),这一步是异步等待的(会让出CPU但必须等结果返回),所以你的print("Here!")要等这一步完成才会执行——而你调用的URL会延迟1秒返回,自然要等1秒才会打印内容。
要让这一步变成非阻塞,你可以先不进入async with上下文,先拿到请求的awaitable对象,之后再手动处理上下文或等待响应。
问题2解答
完全可行,aiohttp支持这种"先发请求、后等待处理"的模式,具体实现步骤如下:
- 发送请求并保存awaitable对象:直接调用
session.get()得到协程对象,或者用asyncio.create_task()把它包装成Task对象(这样请求会立刻在后台被事件循环调度执行)。 - 执行其他工作:此时请求已经处于"飞行中",你可以通过
Task对象的方法(比如done()检查是否完成、pending()检查是否仍在运行)判断请求状态。 - 等待响应并处理:最后await这个
Task或协程对象,拿到响应后再处理(注意要手动关闭响应,或者用async with包裹已就绪的响应)。
给你改好的示例代码:
import aiohttp import asyncio async def main(): async with aiohttp.ClientSession() as session: # 1. 发送请求,包装成Task后台运行 request_task = asyncio.create_task(session.get("https://hub.dummyapis.com/delay?seconds=1")) # 2. 执行其他工作,模拟检查请求状态 for _ in range(3): print(f"请求状态:{'已完成' if request_task.done() else '飞行中'}") await asyncio.sleep(0.3) # 模拟耗时操作 # 3. 等待响应返回并处理结果 async with await request_task as r: print("Here!") text = await r.text() print(text) asyncio.run(main())
这段代码的执行流程:
- 立刻发起请求,随后进入循环检查状态,会打印三次"飞行中"(三次等待共0.9秒,未到URL的1秒延迟时间)
- 1秒后请求完成,进入
async with上下文,打印"Here!"和响应文本
另外,如果不用Task,直接保存协程对象也可以,但用create_task能确保请求在后台被调度执行,是更稳妥的方式。如果不使用async with,拿到响应后记得调用await r.release()释放连接,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Frederik Bode
相关产品推荐
相关产品推荐

