一、代码结构

import asyncio, time
from functools import partial  # 偏函数

async def get_html():
    print('get html start')
    await asyncio.sleep(1)
    return 'hello'

def func(f, url):
    print('send email to cat..')

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    task = loop.create_task(get_html())
    task.add_done_callback(partial)
    loop.run_until_complete(task)

二、关键知识点

1. Task 和 add_done_callback

  • Task 是 asyncio 中包装协程的对象,表示一个正在执行或即将执行的异步操作。
  • add_done_callback(callback):当 Task 执行完毕(无论成功还是异常),会自动调用 callback,并把 Task 本身作为第一个参数传递给 callback。

2. functools.partial

  • partial(func, *args, **kwargs):返回一个新的函数,把 func 的部分参数提前“冻结”。
  • 常用于回调场景,给回调函数预先绑定一些参数。

三、逐行详细解释

1. 导入模块

import asyncio, time
from functools import partial  # 偏函数
  • 导入异步IO、时间模块和偏函数工具。

2. 定义协程

async def get_html():
    print('get html start')
    await asyncio.sleep(1)
    return 'hello'
  • async def get_html():定义一个协程函数。
  • print('get html start'):输出开始信息。
  • await asyncio.sleep(1):异步延时1秒。
  • return 'hello':返回字符串 'hello'

3. 定义回调函数

def func(f, url):
    print('send email to cat..')
  • 普通函数,参数 f(通常是 Future/Task),url 是额外参数。
  • 这里只是简单打印一行信息。

4. 主程序入口

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    task = loop.create_task(get_html())
    task.add_done_callback(partial)
    loop.run_until_complete(task)
  • loop = asyncio.get_event_loop():获取事件循环对象。
  • task = loop.create_task(get_html()):调度协程,返回 Task 对象。
  • task.add_done_callback(partial)
    • 这里有错误
    • partial 是 functools.partial 这个类本身,不是一个回调函数。
    • 正确用法应该是:
      task.add_done_callback(partial(func, url='http://example.com'))
      
    • 这样,func 会在 task 完成后被调用,参数为 (task, url)。
  • loop.run_until_complete(task):运行事件循环,直到 task 完成。

四、正确用法示例

1. 正确绑定回调

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    task = loop.create_task(get_html())
    # partial(func, url) 生成一个新函数,等价于 lambda f: func(f, url)
    task.add_done_callback(partial(func, url='http://example.com'))
    loop.run_until_complete(task)

2. 回调函数的执行

  • get_html() 协程执行完毕,task 完成,事件循环会自动调用 func(task, url)
  • 你可以在回调里通过 task.result() 获取协程的返回值。
def func(f, url):
    print('send email to cat..')
    print('url:', url)
    print('协程返回值:', f.result())

五、完整示例

import asyncio
from functools import partial

async def get_html():
    print('get html start')
    await asyncio.sleep(1)
    return 'hello'

def func(f, url):
    print('send email to cat..')
    print('url:', url)
    print('协程返回值:', f.result())

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    task = loop.create_task(get_html())
    task.add_done_callback(partial(func, url='http://example.com'))
    loop.run_until_complete(task)

输出:

get html start
send email to cat..
url: http://example.com
协程返回值: hello

六、核心原理总结

  • add_done_callback 用于在 Task 完成后自动执行回调,常用于通知、日志、后处理等场景。
  • partial 可以为回调函数预先绑定参数,简化回调签名。
  • 回调函数的第一个参数永远是 Task/Future 本身,可以通过 .result() 获取协程的返回值。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐