Telegraf开发Telegram机器人如何让inline keyboard在所有回复发送后再显示
问题根源
你的代码存在两个核心问题导致键盘提前弹出:
ctx.replyWithVideo是异步方法,返回Promise对象,你使用forEach遍历调用时,不会等待所有异步发送任务执行完成,就直接执行了后续的sendOptionsKeyboard逻辑sendOptionsKeyboard放在了try/catch块外部,即便发送流程出错,也会弹出选择键盘,不符合预期逻辑
可行解决方案
把遍历发送gif的逻辑改为等待所有异步任务完成后,再调用发送键盘的方法,推荐两种实现方式:
方式1:使用for...of串行发送(适合低并发场景,请求顺序可控)
const showSpecificGif = async (ctx, bot) => { try { // 先应答回调查询,避免用户端按钮长时间显示加载状态 await ctx.answerCbQuery() await ctx.reply('Loading...🤔') const offset = Math.floor(Math.random() * 4500) const res = await axios.get( `https://api.giphy.com/v1/gifs/search?api_key=${process.env.GIPHY_API_KEY}&q=${ctx.update.callback_query.data}&limit=3&offset=${offset}&rating=g&lang=en` ) const gifArray = res.data.data // 用for...of等待每一张gif发送完成再发下一张 for (const gif of gifArray) { await ctx.replyWithVideo(gif.images.downsized_medium.url) } // 所有gif发送完成后再弹出选择键盘 await sendOptionsKeyboard(ctx, bot, 'Send again? 😀') } catch (err) { await ctx.reply('You got error') // 出错后也可以选择弹出键盘让用户重试,不需要就删掉下面这行 await sendOptionsKeyboard(ctx, bot, 'Try again? 😀') } }
方式2:使用Promise.all并行发送(适合需要提升发送速度的场景)
如果不需要严格按接口返回的gif顺序发送,可以用Promise.all等待所有发送请求同时完成:
const showSpecificGif = async (ctx, bot) => { try { await ctx.answerCbQuery() await ctx.reply('Loading...🤔') const offset = Math.floor(Math.random() * 4500) const res = await axios.get( `https://api.giphy.com/v1/gifs/search?api_key=${process.env.GIPHY_API_KEY}&q=${ctx.update.callback_query.data}&limit=3&offset=${offset}&rating=g&lang=en` ) const gifArray = res.data.data // 批量生成发送任务,等待所有任务执行完成 const sendTasks = gifArray.map(gif => ctx.replyWithVideo(gif.images.downsized_medium.url)) await Promise.all(sendTasks) await sendOptionsKeyboard(ctx, bot, 'Send again? 😀') } catch (err) { await ctx.reply('You got error') await sendOptionsKeyboard(ctx, bot, 'Try again? 😀') } }
额外优化点
- 所有调用Telegram API的方法最好都加
await,避免出现异步顺序问题 - 回调触发后优先调用
ctx.answerCbQuery(),符合Telegram bot的开发规范,避免用户端出现按钮无响应的报错提示
内容的提问来源于stack exchange,提问作者ylsv
相关产品推荐
相关产品推荐

