Thielicious
Thielicious

Reputation: 4452

Discord bot sends message in the wrong order despite async/await

I'm having this asynchronous coding problem once again, so annoying. I want to avoid using the callback hell and avoid startig with new Promise and resolve so I use async/await instead.

Objective
I want my discord bot to spit out each server status, one after another in a channel, in the same order as defined.

Issue
Of course, the output is in the wrong order. I know that the speed of each server response is different. Despite using async/await it still doesn't work as I wanted and it confuses me. Looks like I'm using it wrong, just don't know where. I tried to iterate the array with a forEach loop and used Promise.all() but it's still not the correct order.

My Code (MCVE)
I am using the module net in order to request the server status and for this MCVE I took 3 random hosts instead.

const 
    status = require('net'),
    hosts = [
        ['Server #1', 'google.com', 80], 
        ['Server #2', 'jhfg87ed4.com', 80], // fake, just for response check
        ['Server #3', 'stackoverflow.com', 80]
    ]

const server = async (id, cb)=> {
    let host = hosts[id]
    const sock = new status.Socket()
    sock.setTimeout(2500)
    sock.on('connect', ()=> {
        cb(host[0]+': Up.')
        sock.destroy()
    }).on('error', e=> {
        cb(host[0]+': Down: '+e.message)
    }).on('timeout', e=> {
        cb(host[0]+' Down: timeout')
    }).connect(host[2], host[1])
}

async function results() { // wrong ?
    await server(0, cb => channel.send(cb))
    await server(1, cb => channel.send(cb))
    await server(2, cb => channel.send(cb))
}

Output: (random order)

results() // not the order I wanted

Server #3: Up.
Server #1: Up.
Server #2: Down: getaddrinfo ENOTFOUND jhfg87ed4.com

Workaround (bad practice)

server(0, cb => {
    channel.send(cb)
    server(1, cb => {
        channel.send(cb)
        server(2, cb => {
            channel.send(cb)
        })
    })
})

Using this workaround fixes it and works like a charm but I want to avoid this callback hell as it's obviously bad practice.

I'd appreciate any help.

Upvotes: 1

Views: 294

Answers (1)

Manuel Spigolon
Manuel Spigolon

Reputation: 12900

There are few errors in the script:

  • async without calling await is not usefull
  • usually a callback is used in place of await, you can't use both
  • the universal callback interface is callback(error, data), so you should follow this pattern

So your script should update as follow.

Note that this is a quick solution that doesn't manage errors! (aka error events)


const status = require('net')
const util = require('util')

const hosts = [
  ['Server #1', 'google.com', 80],
  ['Server #2', 'jhfg87ed4.com', 80], // fake, just for response check
  ['Server #3', 'stackoverflow.com', 80]
]

function server (id, cb) {
  const host = hosts[id]
  const sock = new status.Socket()
  sock.setTimeout(2500)
  sock
    .on('connect', () => {
      cb(null, host[0] + ': Up.') // null as first parameter that is error
      sock.destroy()
    })
    .on('error', e => {
      cb(new Error(host[0] + ': Down: ' + e.message))
    })
    .on('timeout', e => {
      cb(new Error(host[0] + ' Down: timeout'))
    })
    .connect(host[2], host[1])
}

const serverPromise = util.promisify(server)

async function results () {
  let res = await serverPromise(0)
  console.log(res)
  try {
    res = await serverPromise(1)
    console.log(res)
  } catch (error) {
    console.log(error)
  }
  res = await serverPromise(2)
  console.log(res)
}

results()

Upvotes: 2

Related Questions