This is something I've stumbled upon when while trying to make the HTTP server listen
function truly promisified. The biggest problem is not to resolve on listening
callback, but to handle the errors on startup.
Wrapping in Promise and attempt to catch
(as other answers suggest) or try-catch block won't have any effect, because any Node.js server, net
or derived http
/https
, are EventEmitter
instances, which means no errors are thrown. Instead, they are emitted as the error
event.
So, considering all of the above, correct implementation of promisified server listen
function is as follows:
const { createServer } = require('http');
const server = createServer();
const listen = async (port, host) => {
return new Promise((resolve, reject) => {
const listeners = {};
listeners.onceError = (error) => {
server.removeListener('listening', listeners.onceListening);
reject(error);
};
listeners.onceListening = () => {
server.removeListener('error', listeners.onceError);
resolve();
};
server
.prependOnceListener('error', listeners.onceError)
.prependOnceListener('listening', listeners.onceListening);
server.listen(port, host);
});
}
Reject and resolve calls inside handlers are prepended to the top of the listeners stack, and they mutually cancel each other - whoever fires first.
That way it's guaranteed that the listen
method will either start the server or throw a catchable error.
async/await
pattern? Given the code at Question the result would effectively be adding more code to the pattern. – Observant