nest_asyncio.apply()
是怎么工作的,以及为什么我们需要它。
💡背景知识:什么是异步任务 async?
Python 支持两种代码执行方式:
类型 | 示例 | 特点 |
---|---|---|
同步 | 普通函数 def func() | 一件事做完再做下一件 |
异步(async) | 异步函数 async def func() | 支持一边等待一边干别的事 |
比如你要读一个远程文件:
# 同步方式(卡住)
text = read_file("http://example.com")
# 异步方式(不会卡住)
text = await read_file("http://example.com")
异步执行非常适合「等待型任务」(如网络请求、文件解析、大模型调用等),像 LlamaParse
就是异步的。
🤯为什么异步代码不能直接运行?
因为异步代码需要在“事件循环”里运行。
举个例子:
import asyncio
async def say_hello():
print("Hello")
say_hello() # ❌ 直接调用不会执行!
# 正确方式
asyncio.run(say_hello()) # ✅ 在事件循环中运行
但问题是:
如果你已经在一个事件循环里(比如 Jupyter Notebook、某些框架),就 不能再次
asyncio.run()
,否则会报错说“事件循环已存在”。
🐒 什么是猴子补丁(Monkey Patch)?
猴子补丁就是:动态地修改别人的代码行为,不改变源码。
nest_asyncio
就是一个“猴子补丁工具”- 它能 让一个事件循环套娃运行(nest)
- 也就是说:你已经在事件循环里了,它还帮你“嵌套”再开一层循环
🧩 nest_asyncio.apply()
做了什么?
import nest_asyncio
nest_asyncio.apply()
🔧 它悄悄地修改了 asyncio
的运行机制,让你可以这样干:
# 即使你已经在一个事件循环里,也可以再次调用异步代码
asyncio.run(async_function())
或者更常见地:
loop = asyncio.get_event_loop()
result = loop.run_until_complete(async_function())
🧪 举个实际例子:没用和用了的对比
❌ 没有 nest_asyncio:
import asyncio
async def hello():
print("Hi")
# 在 Jupyter 或某些框架中运行下面这行会报错,这是前提
asyncio.run(hello())
错误信息:
RuntimeError: asyncio.run() cannot be called from a running event loop
✅ 用了 nest_asyncio:
import asyncio
import nest_asyncio
nest_asyncio.apply()
async def hello():
print("Hi")
asyncio.run(hello()) # 现在就不会报错了
🧠 这段代码
import nest_asyncio
nest_asyncio.apply()
file_extractor = llama_parse_extractor()
✅ 作用是:
你使用的 LlamaParse
是一个异步的文件解析器,正常应该 await llama_parse.load()
,但写的代码是同步方式运行的脚本,不能直接 await。
所以通过:
nest_asyncio.apply()
你可以在同步的脚本中间,放心大胆地运行 LlamaParse,哪怕它本身是异步的。
🔚 总结一句话:
nest_asyncio.apply()
是一个让「本来只能在异步函数中运行的代码」也能在普通脚本或 Jupyter 中运行的小工具。它让你不用改很多结构,也能用上异步功能,尤其适合用异步解析器如 LlamaParse 的场景。
同步 vs 异步 + nest_asyncio 的对比 demo
- 👉 原生异步函数写法
- ❌ 在同步环境中错误调用的写法
- ✅ 使用
nest_asyncio
成功运行异步任务的写法
✅ 场景说明
我们写一个简单的异步函数 async def fetch_data()
,模拟从服务器获取数据(用 asyncio.sleep()
模拟等待),然后对比几种调用方式。
🧪 示例代码
你可以直接复制粘贴运行在一个 Python 脚本里试试:
import asyncio
import nest_asyncio
# 模拟一个异步函数(比如解析文档或访问API)
async def fetch_data():
print("Start fetching...")
await asyncio.sleep(2) # 模拟耗时操作
print("Data fetched!")
return {"status": "success", "data": [1, 2, 3]}
① ✅ 正确的异步调用方式(在 async 函数里)
# 正确用法:写一个 async 函数,内部使用 await 调用另一个 async 函数
async def main():
result = await fetch_data()
print("Result:", result)
# 启动事件循环来运行 main
asyncio.run(main())
运行效果:
Start fetching...
(等待2秒)
Data fetched!
Result: {'status': 'success', 'data': [1, 2, 3]}
② ❌ 同步环境中直接运行异步函数(会报错)
# 直接调用 async 函数,不在 async 环境中
result = asyncio.run(fetch_data()) # 这在 Jupyter Notebook 或嵌套调用时可能会报错,这是前提
错误(示例):
RuntimeError: asyncio.run() cannot be called from a running event loop
③ ✅ 用 nest_asyncio 让同步环境支持 async 调用
import nest_asyncio
nest_asyncio.apply()
# 模拟你在 Jupyter 或某些框架下想在同步脚本中调用异步函数
loop = asyncio.get_event_loop() # 获取当前事件循环
result = loop.run_until_complete(fetch_data()) # 运行异步函数
print("Result:", result)
输出:
Start fetching...
(等待2秒)
Data fetched!
Result: {'status': 'success', 'data': [1, 2, 3]}
🎯 小结对比
方式 | 场景 | 是否能运行 | 用例 |
---|---|---|---|
asyncio.run(fetch_data()) | 在主程序中 | ✅(除非你已经在事件循环中) | 脚本 |
loop.run_until_complete(...) | 嵌套事件循环或脚本 | ❌(不加补丁会出错) | 框架、Jupyter |
nest_asyncio + loop.run_until_complete(...) | 嵌套事件循环中 | ✅ 推荐用! | Jupyter、同步函数中使用异步 |
🚀 和 LlamaParse 结合
import nest_asyncio
nest_asyncio.apply()
# llama_parse = LlamaParse(...) 是 async 的
# 所以可以这样用:
loop = asyncio.get_event_loop()
result = loop.run_until_complete(parser.load_data_async())