Reputation: 4452
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
Reputation: 12900
There are few errors in the script:
async
without calling await
is not usefullawait
, you can't use bothcallback(error, data)
, so you should follow this patternSo 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