使用Telegraf(Node.js)按顺序发送多条消息的问题
我正在使用Telegraf(Node.js)开发一个按需发送数据的电报机器人,需要按顺序向用户回复媒体(图片/视频)和文本消息。为此我将所需数据收集到Promise数组中,希望按索引0到n的特定顺序发送。
我尝试用递归按顺序解析Promise,但消息仍未按正确顺序送达——文本总是比媒体先到。递归代码如下:
const promiseResolver = async ( promises: Array<Promise<any>>, index = 0 ): Promise<void> => { if (index >= promises.length) { return Promise.resolve(); } await promises[index].then(() => { return promiseResolver(promises, index + 1); }); };
我有一个包含n个Promise的数组,使用Telegraf的replyWithMediaGroup()和reply()方法,如何确保前一个Promise解析完成后再执行下一个?
问题根源
你当前的写法存在核心问题:Promise数组中的每个Promise在创建时就已经发起了Telegram请求,它们会并行执行。递归只是等待这些并行请求完成,但电报服务器对不同类型消息的处理速度有差异(文本消息通常处理更快),所以会出现顺序混乱的情况。
正确实现方式
要保证顺序执行,你需要存储返回Promise的函数,而不是直接存储已执行的Promise。这样每次只有前一个函数执行完成(即消息发送成功),才会触发下一个函数的执行。
步骤1:创建动作函数数组
把每个发送消息的操作封装成独立的函数,这些函数调用Telegraf的方法并返回Promise:
// 示例:根据需求定义动作数组 const messageActions = [ // 发送媒体组 () => ctx.replyWithMediaGroup([ { type: 'photo', media: 'https://example.com/photo1.jpg' }, { type: 'photo', media: 'https://example.com/photo2.jpg' } ]), // 发送媒体后的文本 () => ctx.reply('以上是一组图片'), // 发送视频 () => ctx.replyWithVideo('https://example.com/video.mp4'), // 发送视频后的文本 () => ctx.reply('这是配套的视频说明') ];
步骤2:顺序执行动作函数
可以用两种方式实现顺序执行:
方式一:修改递归函数
调整递归逻辑,每次执行对应的动作函数,而非等待已存在的Promise:
const executeSequentially = async ( actions: Array<() => Promise<any>>, index = 0 ): Promise<void> => { if (index >= actions.length) return; // 先执行当前动作函数,等待完成 await actions[index](); // 递归执行下一个动作 await executeSequentially(actions, index + 1); }; // 调用执行 executeSequentially(messageActions);
方式二:使用for...of循环(更直观)
用异步循环依次执行每个动作函数,代码更简洁易读:
const executeSequentially = async (actions: Array<() => Promise<any>>) => { for (const action of actions) { await action(); } }; // 调用执行 executeSequentially(messageActions);
关键说明
Telegraf的reply()和replyWithMediaGroup()方法,默认会在收到Telegram服务器的成功响应后才会resolve对应的Promise。因此只要保证这些方法按顺序执行,就能确保消息按你期望的顺序发送给用户。
内容的提问来源于stack exchange,提问作者Bar Nachum

