Can You Spot the Difference?
Take a look at these two JavaScript code snippets. They look nearly identical — but do they behave the same?
Snippet 1 (without semicolon):
CODE: https://gist.github.com/velotiotech/5e5a48b7a49a7fd379a17e72be13693f.js
Snippet 2 (with semicolon):
CODE: https://gist.github.com/velotiotech/e6effb614a01397e1338e8ed126d29bc.js
What Happens When You Run Them?
❌ Snippet 1 Output:
TypeError: (intermediate value) is not a function
✅ Snippet 2 Output:
logging result -> printing content of promise1
Why Does a Single Semicolon Make Such a Big Difference?
We’ve always heard that semicolons are optional in JavaScript. So why does omitting just one lead to a runtime error here?
Let's investigate.
What’s Really Going On?
The issue boils down to JavaScript’s Automatic Semicolon Insertion (ASI).
When you omit a semicolon, JavaScript tries to infer where it should end your statements. Usually, it does a decent job. But it's not perfect.
In the first snippet, JavaScript parses this like so:
const promise1 = new Promise(...)(async () => { ... })();
Here, it thinks you are calling the result of new Promise(...) as a function, which is not valid — hence the TypeError.
But Wait, Aren’t Semicolons Optional in JavaScript?
They are — until they’re not.
Here's the trap:
If a new line starts with:
JavaScript might interpret it as part of the previous expression.
That’s what’s happening here. The async IIFE starts with (, so JavaScript assumes it continues the previous line unless you forcefully break it with a semicolon.
🔍 Key Takeaways:
- ASI is not foolproof and can lead to surprising bugs.
- A semicolon before an IIFE ensures it is not misinterpreted as part of the preceding line.
- This is especially important when using modern JavaScript features like async/await, arrow functions, and top-level code.
💡 Why You Should Use Semicolons Consistently
Even though many style guides (like those from Prettier or StandardJS) allow you to skip semicolons, using them consistently provides:
✅ Clarity
You eliminate ambiguity and make your code more readable and predictable.
✅ Fewer Bugs
You avoid hidden edge cases like this one, which are hard to debug — especially in production code.
✅ Compatibility
Not all environments handle ASI equally. Tools like Babel, TypeScript, or older browsers might behave differently.
🧠 Conclusion
The difference between working and broken code here is one semicolon. JavaScript's ASI mechanism is helpful, but it can fail — especially when lines begin with characters like ( or [.
If you're writing clean, modular, modern JavaScript, consider adding that semicolon. It’s a tiny keystroke that saves a lot of headaches.
Happy coding — and remember, when in doubt, punctuate!