IIFEs in JavaScript and how to avoid this common mistake
Published 5/19/2019
Since JavaScript doesn't support top level await quite yet, the typical node index file might look something like this
const http = require('http')
(async () => {
// await ...
})()
We require the http library and then have an immediately invoked function expression(IIFE) just so we can use async await.
With IIFEs we write functions and immediately execute them. This is so everything that is happening within the function stays within the function and is not accessible from outside of it. It is also the way to use await at the top level as of now.
Problem
I am sure many of you have fallen into this trap, as the above code actually breaks.
Uncaught TypeError: require(...) is not a function
The reason it crashes is because JavaScript tries to execute this (try formatting the above code in your editor to get the same result)
const http = require('http')(async () => {
// ...
})()
It expects the require
method to return a function, in which we pass an asynchronous function and then we execute the result of that. π€―
The error becomes especially hard to catch when you have two IIFEs in a row.
Uncaught TypeError: (intermediate value)(...) is not a function
Check out my e-book!
Common workarounds
There are common workarounds for this, which are all about telling JavaScript that the IIFE is indeed a new statement, most notably
const http = require('http')
void (async () => { // < note the void at the beginning
})()
or
const http = require('http'); // < note the semicolon
(async () => {
})()
or even
const http = require('http')
!(async () => { // < note the exclamation mark
})()
Labels
The above workarounds are nothing new, but here is something you might have not seen yet.
const http = require('http')
IIFE: (async () => {
})()
Yup, labels work as well. You can put labels before any statement. We can replace IIFE
with anything we want at this point as long as it follows the syntax. If it works as a variable name, it works as a label identifier.
δΈγε
«γ: 1 + 1
Labels are actually quite interesting. Look at the following code snippet taken from MDN.
foo: {
console.log('this will be executed');
break foo;
console.log('this will not be executed');
}
console.log('this will be executed as well');
Conclusion
Since labels are not so well known, it is probably better to stick with semicolons or void, but it is nonetheless interesting. I like how they add some documentation to IIFEs. Well, let's just wait a little more for top level await.