Jason Sultana

Follow this space for writings (and ramblings) about interesting things related to software development.

NodeJS await is only valid in async function

02 Jul 2021 » javascript, nodejs

G’day guys!

Have you ever been happily coding along in Node, using async functions as the NodeJS gods intented, go to invoke your little script with node ./my-script.js and then boom!, you get something like this?

SyntaxError: await is only valid in async function
    at wrapSafe (internal/modules/cjs/loader.js:1070:16)
    at Module._compile (internal/modules/cjs/loader.js:1120:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1176:10)
    at Module.load (internal/modules/cjs/loader.js:1000:32)
    at Function.Module._load (internal/modules/cjs/loader.js:899:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)
    at internal/main/run_main_module.js:18:47

Lets say that your Node script looked a little something like:

async function doSomethingAsync() {
    return Promise.resolve('Hello, World!');
}

const response = await doSomethingAsync();
console.log(response);

In this case, the error message kind of makes sense. After all, we’re calling an async function with await outside of an async function. But how do we implement a sort of “top-level” async function in Node, when there isn’t an async main method, or something of the like? It is just a script at the end of the day.

The answer is to use an async IIFE (Immediately Invoked Function Expression), like so.

async function doSomethingAsync() {
    return Promise.resolve('Hello, World!');
}

(async function() {
    const response = await doSomethingAsync();
    console.log(response);
})();

Running this script with node ./my-script.js now produces Hello, World! as expected.

That’s a lot of brackets! Are we coding in LISP, or JavaScript!?

I hear ya, the IIFE can look a bit strange if you’ve never seen it before. At the end of the day though, it’s really just a plain old function wrapped in brackets, and then invoked by throwing an extra set of brackets at the end (i.e - to call the function).

Bonus example on IIFEs

The main point of writing this is to show you how to get around using async functions in a top-level node script, but if you’ve followed me this far, you may want to see a bit more on these IIFEs. Well, you’re in luck! In the last section, I mentioned that you throw a set of brackets at the end of the IIFE to invoke it, much like invoking any other function. Well, passing arguments works exactly the same way. Here’s another version of our previous example, modified to accept arguments.

async function printAsync(text) {
    return Promise.resolve(text);
}

(async function(text) {
    const response = await printAsync(text);
    console.log(response);
})('Hello, World!');

Notice how printAsync and our IIFE now accept a text argument, which we provide when we invoke the IIFE.

Anyway, that’s really all I wanted to cover with this post. Hope you’re all enjoying writing cool stuff with Node, and are enjoying the clarity and brevity that async/await brings to the table. Catch ya!