tornado 使用future详解(一)
Tornado 中的
Future 对象是异步编程的核心,它允许你以非阻塞方式处理耗时操作。以下是关于 Tornado Future 的详细解释、最佳实践和示例代码:1. Tornado Future 基础
Future 是一个占位符对象,表示一个异步操作的结果(可能尚未完成)。它类似于 JavaScript 中的 Promise 或 Python 标准库中的 asyncio.Future。关键特性
- 非阻塞:允许其他代码在等待结果时继续执行。
- 回调注册:通过
add_done_callback添加回调函数。 - 协程支持:与
async/await语法无缝结合。
2. 基本用法示例
2.1 手动创建和解析 Future
from tornado.ioloop import IOLoop
from tornado.concurrent import Future
def fetch_data():
future = Future()
# 模拟异步操作(如网络请求)
IOLoop.current().add_timeout(
IOLoop.current().time() + 1,
lambda: future.set_result("Data fetched!")
)
return future
async def main():
result = await fetch_data()
print(result) # 输出: Data fetched!
# 运行协程
IOLoop.current().run_sync(main)
2.2 异步 HTTP 请求
from tornado.ioloop import IOLoop
from tornado.httpclient import AsyncHTTPClient
async def fetch_url(url):
client = AsyncHTTPClient()
response = await client.fetch(url)
return response.body
# 并发请求多个 URL
async def main():
urls = ["https://example.com", "https://google.com"]
results = await asyncio.gather(*[fetch_url(url) for url in urls])
print(results)
IOLoop.current().run_sync(main)
3. 最佳实践
3.1 使用 async/await 而非回调
推荐写法:
async def handle_request():
user = await get_user()
posts = await fetch_posts(user.id)
return {"user": user, "posts": posts}
不推荐写法(回调地狱):
def handle_request():
get_user().add_done_callback(lambda f:
fetch_posts(f.result().id).add_done_callback(lambda p:
# 更多嵌套回调...
)
)
3.2 错误处理
async def fetch_data():
try:
response = await AsyncHTTPClient().fetch("https://invalid-url")
except Exception as e:
print(f"Error: {e}")
return None
return response.body
3.3 并发控制
from tornado import gen
async def process_tasks(tasks):
# 限制同时运行的任务数为 5
semaphore = gen.Semaphore(5)
async def process_task(task):
async with semaphore:
return await task
return await gen.multi([process_task(t) for t in tasks])
3.4 超时处理
from tornado.gen import with_timeout
from tornado.ioloop import IOLoop
import datetime
async def fetch_with_timeout():
try:
# 设置 5 秒超时
result = await with_timeout(
datetime.timedelta(seconds=5),
AsyncHTTPClient().fetch("https://slow-api.com")
)
except TimeoutError:
print("Request timed out")
return result
4. 高级技巧
4.1 自定义异步函数
from tornado.concurrent import return_future
@return_future
def fetch_from_database(query, callback):
# 模拟数据库查询
def on_complete():
result = {"data": "result"}
callback(result) # 结果通过回调传递
IOLoop.current().add_callback(on_complete)
# 使用方式
async def main():
result = await fetch_from_database("SELECT * FROM users")
4.2 与 asyncio 集成
from tornado.platform.asyncio import AsyncIOMainLoop
import asyncio
AsyncIOMainLoop().install() # 将 Tornado 与 asyncio 集成
async def tornado_task():
await AsyncHTTPClient().fetch("https://example.com")
async def asyncio_task():
await asyncio.sleep(1)
# 混合运行 Tornado 和 asyncio 任务
async def main():
await asyncio.gather(tornado_task(), asyncio_task())
asyncio.get_event_loop().run_until_complete(main())
5. 常见错误及避免方法
5.1 阻塞 I/O 操作
错误示例:
async def bad_handler():
# 阻塞操作会挂起整个事件循环
import time
time.sleep(5) # 不要这样做!
正确示例:
async def good_handler():
await asyncio.sleep(5) # 使用异步睡眠
5.2 忘记 await 关键字
async def bad_handler():
future = fetch_data() # 忘记 await,不会等待结果
return "Done"
正确示例:
async def good_handler():
result = await fetch_data()
return result
总结
- 优先使用
async/await:代码更简洁,避免回调地狱。 - 避免阻塞操作:确保所有耗时操作都是异步的。
- 完善错误处理:使用
try/except捕获异常。 - 控制并发数量:使用
Semaphore限制同时运行的任务数。 - 合理设置超时:防止长时间等待无响应的操作。
通过遵循这些实践,你可以充分发挥 Tornado 的异步性能优势,编写出高效、可维护的异步代码。
郭慕荣博客园

浙公网安备 33010602011771号