You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 10:17:31