python 3.7+ 不支持顶层 await,需用 asyncio.run() 封装协程在模块导入时执行一次初始化,或在 __init__.py/入口脚本中集中调度,辅以同步兜底与懒加载优化冷启动。

Python 3.7+ 并不支持真正的顶层 await(即在模块级、非函数作用域直接写 await some_coro()),这是语言层面的硬性限制。所谓“优雅启用”,本质是绕过语法禁令,用语义等价、工程稳健的方式达成相同目标:在模块加载初期就完成关键异步初始化(如配置加载、连接预热、认证令牌获取),从而缩短后续请求的冷启动延迟。
用 async def + asyncio.run() 封装初始化逻辑
这是最直接、兼容性最好、无副作用的做法。把需 await 的操作封装为协程函数,再在顶层调用 asyncio.run() 执行一次:
- 确保只执行一次:利用模块导入机制的单次性,
asyncio.run()在模块首次 import 时触发,之后复用结果 - 避免阻塞后续 import:所有依赖该模块的代码会等待初始化完成后再继续,语义清晰
- 示例:
import aiohttp
async def _init_config():
async with aiohttp.ClientSession() as session:
resp = await session.get("https://api.example.com/config")
return await resp.json()
# 顶层执行
CONFIG = asyncio.run(_init_config())
借助 __init__.py 或入口脚本统一调度
若模块本身不适宜直接运行异步初始化(如被多处 import,但初始化只需一次),可将异步 setup 提前到包的 __init__.py 或应用主入口中集中处理:
- 在
myapp/__init__.py中定义async def init(),并在文件末尾调用asyncio.run(init()) - 或在
main.py开头显式 await 初始化,再导入业务模块 —— 这样能控制时机,也便于加超时、重试 - 优势:解耦初始化逻辑与业务模块,利于测试和替换
用同步替代方案兜底关键路径
并非所有“需要 await”的场景都必须异步。冷启动中真正拖慢的是 I/O 等待,而很多初始化其实可转为同步或懒加载:
- 配置文件读取 → 改用
json.load(open(...)),快且确定 - 环境变量或本地 secret → 同步获取,无需网络
- 数据库连接池 → 保持全局单例,连接在首次 query 时建立(懒连接),而非启动时强制 await
- 第三方 token 获取 → 若有缓存(如 Redis),先查同步缓存;无则 fallback 到异步刷新,但主流程不阻塞
警惕陷阱:别用线程模拟顶层 await
有人尝试用 threading.Thread(target=asyncio.run, args=(coro,)).start() 来“后台执行”,这不可取:
- 事件循环未被正确管理,容易引发 RuntimeError 或静默失败
- 主线程无法可靠获知初始化是否完成,导致后续逻辑读到空/默认值
- 破坏模块导入顺序语义,调试困难
- Serverless 环境下线程可能被中断,状态丢失











