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 的异步性能优势,编写出高效、可维护的异步代码。
posted @ 2025-06-22 08:21  郭慕荣  阅读(33)  评论(0)    收藏  举报