How do I wrap a callback with async await?
Asked Answered
E

5

18

My function returns a promise that resolves as soon as the HTTP server starts. This is my code:

function start() {
    return new Promise((resolve, reject) {
        this.server = Http.createServer(app);
        this.server.listen(port, () => {
            resolve();
        });
    })
}

How do I convert the start function to async/await?

Euripus answered 25/10, 2017 at 13:30 Comment(1)
Why do you want to convert the code to async/await pattern? Given the code at Question the result would effectively be adding more code to the pattern.Observant
O
19

Include async before the function declaration and await the Promise constructor. Though note, you would essentially be adding code to the existing pattern. await converts a value to a Promise, though the code in the question already uses the Promise constructor.

async function start() {
    let promise = await new Promise((resolve, reject) => {
        this.server = Http.createServer(app);
        this.server.listen(port, () => {
            resolve();
        });
    })
    .catch(err => {throw err});

    return promise
}

start()
.then(data => console.log(data))
.catch(err => console.error(err));
Observant answered 25/10, 2017 at 13:34 Comment(8)
You merely wrapped the promise. I want to get rid of the callback.Scopas
You cannot "get rid of the callback" simply by using Promise or async/awaitObservant
You would need to re-write the code base of Http.createServer() to remove the callback pattern from the codeObservant
Well i think you answered my question... In fact i didn't really had a question... I think i am just having problems accepting that this async await feature is completely useless, shallow and is just a bit of syntax sugar. Why would they add this to the language if it doesn't replace the previous asynchronous mechanisms?Scopas
@TiagoBértolo async was mean to make promise chains behave more like synchronous code. It's sugar, but very useful sugar (for loops, while loops, if statements, and try/catch work with promises now)Tinfoil
Well my while loops will have async instead of thens? So what? It is just sugar. It is going to annoy and confuse new programmers and it does not solve my real problems or add new features like RXJS does.Scopas
@TiagoBértolo: other languages have async/await as well, so it’s not something uncommon. It can make code a lot more readable. Alone marking a function as async tells the reader that this function returns a promise, without even having to look at the return statement. But being able to use try...catch and throw is probably an even bigger advantage. But as always, if you don’t like it you don’t have to use it :)Citify
The .catch(...) will never fire if you don't call reject() from within the Promise, for god's sake at least reject on this.server.on("error", ...).Sherris
T
12

Creating a new Promise like the other answers suggest works fine in this case, but as a general rule, util.promisify can stop you from writing the same thing many times.

So you can do something like this instead: (node.js v8.0.0+)

const util = require('util');
async function start() {
    let server = Http.createServer(app);
    await util.promisify(server.listen.bind(server))(port);
}

util.promisify(some_function) takes a function which normally accepts a callback, and returns a new, wrapped version of this function that instead returns a promise.

With more explained steps:

let server = Http.createServer(app);
// .bind() is needed so that .listen() keeps the correct `this` context when it is called.
// If your function does not require any specific context, leave off .bind()
let listen_promise = util.promisify(server.listen.bind(server));
await listen_promise(port);

More advanced promisification can be done with bluebird.

Tinfoil answered 25/10, 2017 at 13:44 Comment(2)
Nice, clean, and simple. util.promisify().Abhor
Great solution always simpleRoca
F
5

Use:

const doRequest = () => new Promise((resolve, reject) {
    this.server = Http.createServer(app);
    this.server.listen(port, () => {
        resolve();
    });
})

async function start() {
    await doRequest()
}

Something like this, I believe.

Few answered 25/10, 2017 at 13:34 Comment(0)
F
2

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.

Foregoing answered 7/7, 2018 at 19:46 Comment(0)
C
-1

I created a basic utility that may not be the most proper way to do, but it is way more readable, in my opinion:

// Async time-out utility
const timeout = async ms => new Promise(res => setTimeout(res, ms));

async function start() {
    let output;

    this.server = Http.createServer(app);
    this.server.listen(port, () => {
        output = true; // Or can be any data that you want to return
    });
    while (output === undefined) await timeout(10);
    return output;
}

This is the basic concept. However be careful if your promise may return undefined values has the function will run forever (but this will not crash).

Comity answered 16/4, 2019 at 16:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.