Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / Node.js

NodeJS Await is Only Valid in Async Function

5.00/5 (2 votes)
31 Jul 2021CPOL2 min read 29.3K  
Implement a “top-level” async function in Node when there is no async main method

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

Let's say that your Node script looked a little something like:

JavaScript
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.

JavaScript
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.

JavaScript
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 clarify and brevity that async/await brings to the table. Catch ya!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)