javascriptnode.jspromisenode-promisify

Can't promisify callback based function


I want to use the library aztro-js where a typical call in their docs looks like this:

const aztroJs = require("aztro-js");

//Get all horoscope i.e. today's, yesterday's and tomorrow's horoscope
aztroJs.getAllHoroscope(sign, function(res) {
   console.log(res);
});

For several reasons, I would like to use it using async/await style and leverage try/catch. So I tried promisify like this:

const aztroJs = require("aztro-js");
const {promisify} = require('util');
const getAllHoroscopeAsync = promisify(aztroJs.getAllHoroscope);

async function handle() {
  let result, sign = 'libra';
  try {
    result = await getAllHoroscopeAsync(sign);
  }
  catch (err) {
    console.log(err);
  }
  console.log("Result: " + result);
}

However, when I log result it comes as undefined. I know the call worked since the library is automatically logging a response via console.log and I see a proper response in the logs.

How can I "await" on this call? (even by other means if this one is not "promisifyable")


Solution

  • util.promisify() expects the callback function to accept two arguments, the first is an error that must be null when there is no error and non-null when there is an error and the second is the value (if no error). It will only properly promisify a function if the callback follows that specific rule.

    To work around that, you will have to manually promisify your function.

    // manually promisify
    aztroJs.getAllHoroscopePromise = function(sign) {
        return new Promise(resolve => {
            aztroJs.getAllHoroscope(sign, function(data) {
                resolve(data);
            });
        });
    };
    
    // usage
    aztroJs.getAllHoroscopePromise(sign).then(results => {
        console.log(results);
    });
    

    Note, it's unusual for an asynchronous function that returns data not to have a means of returning errors so the aztroJs.getAllHoroscope() interface seems a little suspect in that regard.

    In fact, if you look at the code for this function, you can see that it is making a network request using the request() library and then trying to throw in the async callback when errors. That's a completely flawed design since you (as the caller) can't catch exceptions thrown asynchronously. So, this package has no reasonable way of communicating back errors. It is designed poorly.