164

When using a simple callback such as in the example below:

test() {
  api.on( 'someEvent', function( response ) {
    return response;
  });
}

How can the function be changed to use async / await? Specifically, assuming 'someEvent' is guaranteed to be called once and only once, I'd like the function test to be an async function which does not return until the callback is executed such as:

async test() {
  return await api.on( 'someEvent' );
}
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
sean2078
  • 5,131
  • 6
  • 32
  • 32
  • 1
    Just for reference, the ES7/ES2016 specification has been finalized and it doesn't include async/await. At the moment it's just a [stage 3 proposal](https://github.com/tc39/ecma262#current-proposals). – Dan Prince May 08 '16 at 20:10
  • Well that's surprising - Very much hope it gets included! Thanks for the info @DanPrince – sean2078 May 08 '16 at 20:23

6 Answers6

228

async/await is not magic. An async function is a function that can unwrap Promises for you, so you'll need api.on() to return a Promise for that to work. Something like this:

function apiOn(event) {
  return new Promise(resolve => {
    api.on(event, response => resolve(response));
  });
}

Then

async function test() {
  return await apiOn( 'someEvent' ); // await is actually optional here
                                      // you'd return a Promise either way.
}

But that's a lie too, because async functions also return Promises themselves, so you aren't going to actually get the value out of test(), but rather, a Promise for a value, which you can use like so:

async function whatever() {
  // snip
  const response = await test();
  // use response here
  // snip
}
Madara's Ghost
  • 172,118
  • 50
  • 264
  • 308
13

It's annoying that there isn't a straightforward solution, and wrapping return new Promise(...) is fugly, but I have found an ok work-around using util.promisify (actually it also kinda does the same wrapping, just looks nicer).

function voidFunction(someArgs, callback) {
  api.onActionwhichTakesTime(someMoreArgs, (response_we_need) => {
    callback(null, response_we_need);
  });
}

The above function does not return anything, yet. We can make it return a Promise of the response passed in callback by doing:

const util = require('util');

const asyncFunction = util.promisify(voidFunction);

Now we can actually await the callback.

async function test() {
  return await asyncFunction(args);
}

Some rules when using util.promisify

  • The callback must be the last argument of the function that is gonna be promisify
  • The supposed-callback must be in the form (err, res) => {...}

Funny thing is we do not need to ever specifically write what's the callback actually is.

ErikD
  • 131
  • 1
  • 3
  • Important to note that `'util'` is in node js: https://nodejs.org/api/util.html#utilpromisifyoriginal so this solution won't work on the front end. – Luke Jul 27 '22 at 15:42
11

async/await is magic. You can create a function asPromise to handle this kind of situations:

function asPromise(context, callbackFunction, ...args) {
    return new Promise((resolve, reject) => {
        args.push((err, data) => {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
        if (context) {
            callbackFunction.call(context, ...args);
        } else {
            callbackFunction(...args);
        }
    });
}

and then use it when you want:

async test() {
    return await this.asPromise(this, api.on, 'someEvent');
}

the number of args is variable.

YakovL
  • 7,557
  • 12
  • 62
  • 102
negstek
  • 615
  • 8
  • 29
5
const getprice = async () => {
return await new Promise((resolve, reject) => {
    binance.prices('NEOUSDT', (error, ticker) => {
        if (error) {
            reject(error)
        } else {
            resolve(ticker);
        }
    });
})}

router.get('/binanceapi/price', async function (req, res, next) {
res.send(await binanceAPI.getprice());});
sema toghem
  • 77
  • 1
  • 4
  • 1
    Hi @sema, Welcome to StackOverflow. Please go through the below doc on how to write a good answer which will help you fetch attention :- https://stackoverflow.com/help/how-to-answer – Pratap Alok Raj Jun 04 '21 at 03:01
3

You can achieve this without callbacks , use promise async await instead of callbacks here how I would do this. And also here I have illustrated two methods to handle errors

clickMe = async (value) => {
  
  // begin to wait till the message gets here;
  let {message, error} = await getMessage(value);
  
  // if error is not null
  if(error)
    return console.log('error occured ' + error);
   
  return console.log('message ' + message);

}

getMessage = (value) => {

  //returning a promise 
  return new Promise((resolve, reject) => {
  
    setTimeout(() => {
      // if passed value is 1 then it is a success
      if(value == 1){
        resolve({message: "**success**", error: null});
      }else if (value == 2){
        resolve({message: null, error: "**error**"});
      }
    }, 1000);
  
  });

}

clickWithTryCatch = async (value) => {

  try{
    //since promise reject in getMessage2 
    let message = await getMessage2(value);
    console.log('message is ' + message);
  }catch(e){
    //catching rejects from the promise
    console.log('error captured ' + e);
  }

}

getMessage2 = (value) => {

  return new Promise((resolve, reject) => {
  
    setTimeout(() => {
      if(value == 1)
        resolve('**success**');
      else if(value == 2)
        reject('**error**'); 
    }, 1000);
  
  });

}
<input type='button' value='click to trigger for a value' onclick='clickMe(1)' />
<br/>
<input type='button' value='click to trigger an error' onclick='clickMe(2)' />
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(1)'/>
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(2)'/>
noone
  • 6,168
  • 2
  • 42
  • 51
0

Define a function returning promise:

export const callbackf = (f, ...args) => new Promise((resolve, reject) => {
    f(...args, (error, data) => {
        if (error) {
            reject(error);
        } else {
            resolve(data);
        }
    });
});

Await for the promise anywhere you need. arg1, arg2, ... are the callback fucntion arguments:

await callbackf(yourCallbackFunction, arg1, arg2, arg3, argN)