You need to enable JavaScript to run this app. async函数中如何等待用户回复后执行.then?是否需拆分替代.then
优惠活动
大模型
产品
解决方案
定价
更多

async函数中如何等待用户回复后执行.then?是否需拆分替代.then

核心问题定位
  • ctx.reply() 返回的Promise触发resolve的时机,仅代表「带年龄选择键盘的提示消息成功投递到用户聊天窗口」,完全不代表「用户完成了年龄选择、发送了回复消息」。原代码中链式调用的.then()会在消息发送成功后立刻执行,此时拿到的ctx.message.text还是触发x函数的旧消息内容,根本不是用户后续选择的年龄值,自然实现不了“等用户选完再写库”的效果。
  • 这类交互无法靠单次.then()链式调用实现,本质是因为机器人的消息交互是离散的事件流,不是单次请求-响应的闭环,必须把“等待用户下一步操作”的环节从原函数中拆分,通过状态标记衔接后续的消息处理逻辑。
落地实现思路
  • 维护一份用户的流程状态标记:因为机器人会同时处理多用户的消息,必须给每个聊天会话记录当前所处的流程节点(比如是否正在等待用户选年龄),状态可以存在本地内存Map,也可以直接存在数据库里做持久化。
  • 单独注册全局消息拦截中间件:所有用户发送的新消息先过这个中间件判断,如果当前用户的状态是“等待选择年龄”,就拦截这条消息,校验内容是否是预设的年龄选项,校验通过就执行数据库写入逻辑,写完清除该用户的等待状态;校验不通过就提示重新选择。
  • 拆分原函数的职责:原x函数只保留两个核心动作——发送带年龄键盘的提示消息、给当前触发函数的用户打上“等待选择年龄”的状态标记,把原.then()里的数据库写入逻辑完全从这部分代码中剥离。
改造后的参考代码
// 存储用户等待状态,生产环境可替换为数据库/Session存储
const userWaitingState = new Map()

// 原触发年龄选择流程的函数
async function ageSelectFlow(ctx) {
    // 1. 发送带选择键盘的提示
    await ctx.reply(
        'Select your age',
        Markup.keyboard([
            ['10'],
            ['11'],
            ['12'],
        ])
        .oneTime()
        .resize()
    )
    // 2. 标记当前用户进入等待年龄选择的状态
    userWaitingState.set(ctx.message.chat.id, 'wait_select_age')
}

// 全局消息拦截中间件,需注册在所有业务路由之前
bot.use(async (ctx, next) => {
    const currentChatId = ctx.message?.chat?.id
    // 判断当前会话是否处于等待年龄选择的状态
    if (currentChatId && userWaitingState.get(currentChatId) === 'wait_select_age') {
        const selectedValue = ctx.message.text
        // 校验用户选择的内容是否为预设的合法年龄选项
        if (!['10', '11', '12'].includes(selectedValue)) {
            await ctx.reply('请点击键盘上的选项完成年龄选择')
            return
        }
        // 执行原逻辑里的数据库写入
        await admin
            .database()
            .ref(`users/${currentChatId}`)
            .set({
                username: ctx.message.from.first_name,
                rank: selectedValue,
            })
        // 写入完成后清除等待状态,放行后续的正常业务消息
        userWaitingState.delete(currentChatId)
        return
    }
    // 非等待状态的消息,正常走业务路由处理
    await next()
})
补充说明
  • 如果不想手动维护状态Map,可以直接使用机器人框架自带的Session能力,把等待状态存在会话对象里,逻辑和上面的实现完全一致,多实例部署场景下也不需要额外改状态同步逻辑。
  • 所有需要等用户完成多步选择的场景,都是靠“流程状态标记+全局消息拦截”的模式实现,和单轮请求的Promise链式调用逻辑存在本质区别,不要把发送消息的Promise完成时机和用户交互完成时机做混淆。

内容的提问来源于stack exchange,提问作者tngrj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.04 11:06:11