DEV Community

loading...

IIFEs in JavaScript and how to avoid this common mistake

Michael Z
Software writer
Originally published at michaelzanggl.com ・Updated on ・2 min read

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.

Discussion (3)

Collapse
craigmc08 profile image
Craig McIlwrath

It seems quite inconsistent to just use a semicolon in this specific case and no where else. In fact, this is a really good example of why you should use semicolons everywhere: to avoid strange errors caused by the implicitly inserted semicolons.

Do you have a reason why you don't use semicolons?

Collapse
michi profile image
Michael Z Author

I really don't mind the use of semicolons, but personally haven't used them for some time now. It removes some clutter from the code and especially makes chains ([].map.filter etc.) easier to extend.

I am not fully convinced to use semicolons everywhere just for the edge case when a line starts with paranthesis or brackets, but perfectly understand when people do so. There are of course other good reasons to use semicolons.

But you can't switch ASI off, so anyways people have to learn about it. Because even with the use of semicolons you will run into things like this.

return
  {
    city: 'London'
  };

The above returns undefined because ASI places a semicolon after the return.

Nowadays there are linters and tools like prettier that can really help with these problems.