python-telegram-bot异步命令线程包装器引发NoneType await异常
问题分析
报错的核心原因是:你写的asyncbot装饰器返回的是None,而python-telegram-bot v20+的命令处理器要求返回可被await的协程对象。当框架尝试await这个处理器时,发现拿到的是None,直接触发TypeError: object NoneType can't be used in 'await' expression。
另外你的实现思路完全走偏了:python-telegram-bot基于asyncio构建,处理耗时任务根本不需要手动开线程,强行这么做反而会破坏asyncio的事件循环机制,还可能引发线程安全问题(官方明确说库不保证线程安全)。
正确解决方案
分两种场景给出对应处理方式:
场景1:IO密集型耗时任务(比如网络请求、文件读写)
直接用asyncio.create_task把任务丢到后台执行,不会阻塞主事件循环:
import asyncio from telegram import Update from telegram.ext import ContextTypes async def test(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: # 先给用户发即时响应,避免用户以为Bot没反应 await update.message.reply_text("任务已启动,完成后会通知你") # 创建后台异步任务 asyncio.create_task(long_running_io_job(update, context)) async def long_running_io_job(update: Update, context: ContextTypes.DEFAULT_TYPE): # 模拟IO耗时操作,必须用async版本的sleep(如果是真实IO,也要用异步库) await asyncio.sleep(20) # 任务完成后通知用户 await update.message.reply_text("任务执行完毕!")
场景2:CPU密集型耗时任务(比如大量计算)
CPU密集任务会阻塞asyncio事件循环,这时候可以用asyncio.to_thread(Python3.9+)或者线程池来把任务放到单独线程执行:
方式一:用asyncio.to_thread(推荐)
import asyncio import time from telegram import Update from telegram.ext import ContextTypes async def test(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: await update.message.reply_text("CPU密集任务开始执行") # 把CPU任务丢到线程中执行 await asyncio.to_thread(long_running_cpu_job, update, context) await update.message.reply_text("CPU密集任务完成") def long_running_cpu_job(update: Update, context: ContextTypes.DEFAULT_TYPE): # 模拟CPU密集操作,用普通time.sleep即可 time.sleep(20) # 注意:如果要在这个线程里调用Telegram API,建议通过主线程的回调处理,避免线程安全问题
方式二:自定义线程池
import asyncio import time from concurrent.futures import ThreadPoolExecutor from telegram import Update from telegram.ext import ContextTypes # 全局线程池,根据服务器配置调整worker数量 executor = ThreadPoolExecutor(max_workers=4) async def test(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: await update.message.reply_text("CPU密集任务开始执行") loop = asyncio.get_running_loop() # 提交任务到线程池 await loop.run_in_executor(executor, long_running_cpu_job, update, context) await update.message.reply_text("CPU密集任务完成") def long_running_cpu_job(update: Update, context: ContextTypes.DEFAULT_TYPE): time.sleep(20)
原代码的问题拆解
- 装饰器返回None:你的
asyncbot装饰器里,wrapper函数没有返回值,默认返回None。而python-telegram-bot调用处理器时,期望得到一个协程对象,await None自然会报错。 - 错误的线程使用方式:手动创建Thread并在里面调用
asyncio.run,会在新线程启动独立的asyncio事件循环,既浪费资源,又违反了库的线程安全要求,完全没必要。
内容的提问来源于stack exchange,提问作者gang tang
相关产品推荐
相关产品推荐

