In earlier videos we saw how synchronous calls and asynchronous calls vary widely in terms of execution order and exception propagation; this brings us to Zalgo.
Zalgo is an internet meme about a major demonic creature, kinda like Chtulhu, if that rings a bell.
In August 2013, Isaac Schlueter, the creator of npm, wrote a blog post explaining why you should never, ever, write an API that invokes its callback sometimes synchronously, sometimes asynchronously.
As a joke, he posited that releasing that kind of API was tantamount to unleashing Zalgo upon the world.
And indeed, synchronous and asynchronous APIs are completely different beasts. An API that behaves now one way and then another would force the calling code to cater to two entirely distinct approaches, which quickly turns into a nightmare about He Who Waits Beyond The Wall, you could say.
Let's see that in practice.
Code example: 06-zalgo.js
This program wanted to help performance by caching calls (also known as memoization). This is a common cause for Zalgo.
The tryTooHard() function will make some network requests based on the provided key, then cache it. Its processing is intrinsically asynchronous, and the first time around, as nothing is cached, it will indeed invoke its callback asynchronously.
The next time around, it gets its response right out of the cache, and inadvertently invokes its callback synchronously. Zalgo !
The call sequence inside the calling code won’t be the same the first time around compared to all later times. Same for exception handling.
(demo run-throughs of both situations)
To fix this, we just need to ensure the callback is always invoked asynchronously, even for a cache hit. We’re opting here for a universal solution with setTimeout(…, 0). Depending on your environment, you may have other, more immediate APIs available for this.
The golden rule takeaway is: an API that is sometimes asynchronous should ALWAYS be asynchronous.