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