German
German

Reputation: 10402

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")

Upvotes: 1

Views: 2862

Answers (3)

Jain
Jain

Reputation: 1249

Try custom promisified function

aztroJs.getAllHoroscope[util.promisify.custom] = (sign) => {
  return new Promise((resolve, reject) => {
    aztroJs.getAllHoroscope(sign, resolve);
  });
};

const getAllHoroscopeAsync = util.promisify(aztroJs.getAllHoroscope);

Upvotes: 3

jfriend00
jfriend00

Reputation: 707876

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.

Upvotes: 4

f4zr
f4zr

Reputation: 70

You could change your getAllHoroscopeAsync to a promise function

Example:

const getAllHoroscopeAsync = (sign) => 
    new Promise(resolve => 
            aztroJs.getAllHoroscope(sign, (res) => resolve(res)));

Upvotes: 1

Related Questions