1

I'm writing a function to generate a random key using crypto.randomBytes, which takes a callback. I'd prefer to use async await so I'm trying to use util.promisify to wraprandom bytes like this:

const crypto = require('crypto');
const util = require('util');

const randBytes = util.promisify(crypto.randomBytes);

async function genRandKey() {

  bytes = await randBytes(48).catch((err) => {
    console.log(err);
  });

  return bytes.toString('hex');
}

let result = genRandKey();
console.log('key: ', result);

But this prints key: Promise { <pending> } instead of printing the resolved value. What am I doing wrong here?

Huggzorx
  • 144
  • 1
  • 13

2 Answers2

3

All async functions return a promise., So your genRandKey() function returns a promise too. You have to use await or .then() on the result from genRandKey(). Just because you converted to a promise and used await does not mean you can directly return the value from the function. That's not what is happening in an async function. The return value in an async function just becomes the resolved value of the promise that the function returns. While, the code looks like you are returning the value directly, that's not what is actually happening.

In Javascript/node.js, there is NO way to take an asynchronously retrieved value and return it directly from a function. It has to be communicated back via a promise or a callback or an event. There is no way around it.


Now, in this specific case, there IS a synchronous version of crypto.randomBytes() and you could use that instead. The asynchronous version exists for a reason because crypto.randomBytes() takes a little while to run and if you use the synchronous version, it will block the event loop while it is running. Depending upon exactly what you are doing, this may or may not be a problem . The asynchronous version of crypto.randomBytes() runs the actual crypto operations in a separate thread (using the libuv thread pool) and returns the value asynchronously so it doesn't block the event loop.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Yep, I realise I was just being dumb trying to use it directly like that in the top-level of my toy program, whereas in the application the call would itself be from a different async function so I can just await the result of the promise there. – Huggzorx Apr 30 '20 at 21:59
3

The async function genRandKey() is being called synchronously, so it will return a Promise. You can use the .then() function to write to the console after the function has completed. You need to change the following code:

let result = genRandKey();
console.log('key: ', result);

to

genRandKey().then((result) => {
    console.log('key: ', result);
});

However, this will cause the function to be called asynchronously while the rest of your code runs. A solution could be to wrap your whole program in a self-executing async function and use the await keyword:

(async () => {
    const crypto = require('crypto');
    const util = require('util');

    const randBytes = util.promisify(crypto.randomBytes);

    async function genRandKey() {
        bytes = await randBytes(48).catch((err) => {
            console.log(err);
        });

        return bytes.toString('hex');
    }

    let result = await genRandKey();
    console.log('key: ', result);
})();

Alternatively, you could just put the rest of the code in the .then() function:

const crypto = require('crypto');
const util = require('util');

const randBytes = util.promisify(crypto.randomBytes);

async function genRandKey() {
    bytes = await randBytes(48).catch((err) => {
        console.log(err);
    });

    return bytes.toString('hex');
}

genRandKey().then((result) => {
    console.log('key: ', result);

    ...rest of code...
});
Daemon Beast
  • 2,794
  • 3
  • 12
  • 29
  • Uhh, `while (block) {};` is exactly what does NOT work in Javascript. That just hangs the event loop. Since it can never get back to the event loop, it can never process the event that will change the value of `block` to false. This simply won't work. All you did with that part of the answer was illustrate an infinite loop and illustrate exactly how NOT to wait for asynchronous results in Javascript. The rest of the answer is fine. – jfriend00 May 01 '20 at 00:36
  • 1
    For a further explanation of the infinite loop and blocking the event loop see [How can we block event loop](https://stackoverflow.com/questions/61358303/how-can-we-block-event-loop/61358589#61358589) and [Wait until flag is true](https://stackoverflow.com/questions/22125865/wait-until-flag-true/22125978#22125978) and [Why does a while loop block the event loop](https://stackoverflow.com/questions/34824460/why-does-a-while-loop-block-the-event-loop/34825352#34825352). – jfriend00 May 01 '20 at 00:39