telegraf / telegraf

Modern Telegram Bot Framework for Node.js
https://telegraf.js.org
MIT License
8.25k stars 928 forks source link

async middleware function breaks the expected middleware execution order #1228

Closed wernerwws closed 3 years ago

wernerwws commented 3 years ago

I register 3 middlewares:

async function asyncFunction() {
  return true
}

bot.use((ctx: any, next: Function) => {
  console.log('1')
  next().then(console.log('1 then'))
})

bot.use(async (ctx: any, next: Function) => {
  await asyncFunction()
  console.log('2')
  next().then(console.log('2 then'))
})

bot.use((ctx: any, next: Function) => {
  console.log('3')
  next().then(console.log('3 then'))
})

and expect:

1
2
3
1 then
3 then
2 then

but get:

1
1 then
2
3
3 then
2 then

Is this the intended behavior?

In my particular use case, I register a DynamoDB session middleware as the first middleware and expect this middleware to synchronize with DynamoDB after the last middleware is finished.

In the DynamoDB session middleware this is done in /lib/session.js#L106 with return next().then(() => this.saveSession(key, session)).

wojpawlik commented 3 years ago

You were using .then wrong, and your type annotations are worse than no annotations, fixed it for you:

const { Composer } = require('telegraf')

async function asyncFunction () {
  return true
}

const c = new Composer()

c.use(async (ctx, next) => {
  console.log('1')
  await next()
  console.log('1 then')
})

c.use(async (ctx, next) => {
  await asyncFunction()
  console.log('2')
  await next()
  console.log('2 then')
})

c.use(async (ctx, next) => {
  console.log('3')
  await next()
  console.log('3 then')
})

module.exports = c

Now it outputs:

1
2
3
3 then
2 then
1 then

This is the expected behavior.

wernerwws commented 3 years ago

Thank you very much for your fast help! This works for me.

Can you give me a hint why it works with the above mentioned DynamoDB session middleware, which uses return next().then() in lib/session.js#L106? I tried to understand what Composer is doing under the hood, but it was too complicated for me.

wojpawlik commented 3 years ago

Because it passes a function to .then, not result of calling it (which happens to be undefined in your case).