IIFEs in JavaScript and how to avoid this common mistake

Michael Z - May 9 '19 - - Dev Community

Originally posted at michaelzanggl.com. Subscribe to my newsletter to never miss out on new content.

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 ...
})()
Enter fullscreen mode Exit fullscreen mode

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 () => {
  // ...
})()
Enter fullscreen mode Exit fullscreen mode

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

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

})()
Enter fullscreen mode Exit fullscreen mode

or

const http = require('http'); // < note the semicolon

(async () => {

})()
Enter fullscreen mode Exit fullscreen mode

or even

const http = require('http')

!(async () => { // < note the exclamation mark

})()
Enter fullscreen mode Exit fullscreen mode

Labels

The above workarounds are nothing new, but here is something you might have not seen yet.

const http = require('http')

IIFE: (async () => {

})()
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player