Don't Unleash Zalgo in your Node.js Application!
Tags: node, webdev, javascript, tutorial
Published: 2023-01-12 11:57:12

**Don't Unleash Zalgo in your Node.js application**. 

That's my sincere advice.

And I believe that you can easily avoid the traps of Zalgo while building your next API. But for that, you need to understand how it looks like.

In the real world, the term Zalgo comes from a meme. It happens to be an Internet legend about an ominous entity believed to cause insanity, death and destruction of the world. Zalgo is often associated with scrambled text on web pages and photos of people whose eyes and mouths have been covered in black.

In the matrix-world of software development, Zalgo is a piece of code that can destroy your application. 

And you won't even know what's going on.

![meme-on-zalgo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ukc3gq86qb94k8atgbmd.png)

## The First Signs of Zalgo - Confused Code

Callbacks make it possible for JavaScript to handle concurrency despite being single-threaded (technically).

This is what essentially drives [concurrency in Node.js](https://progressivecoder.com/nodejs-reactor-pattern-event-loop/). 

You can build callbacks that behave **synchronously** or **asynchronously**. Typically, you choose only one approach.

The problem shows up when you end up choosing both - **sync** and **async**.

**In other words, what if you develop a function that runs synchronously in certain conditions and asynchronously in some other conditions?**

It's an unpredictable function.

**Unpredictable functions and the APIs built using them unleash Zalgo in your code**.

## Example

Here's an example:

```js
let cache = {};

function getStringLength(text, callback) {
    if (cache[text]) {
        callback(cache[text])
    } else {
        setTimeout(function() {
            cache[text] = text.length;
            callback(text.length)
        }, 1000)
    }
}
```

Despite the innocent appearance, the `getStringLength()` function is evil. 

Though it is used to simply calculate the length of a string, it has two faces.

If the string and its length are available in the `cache` object, the function behaves synchronously by returning the data right away from the cache.

Otherwise, it calculates the length of the string and stores the result in the `cache` before triggering the callback. The calculation happens asynchronously using a `setTimeout()`.

Do note that the use of `setTimeout()` is to force an asynchronous behaviour. You can replace it with any other asynchronous activity such as reading a file or making an API call. The idea is to demonstrate that a function can have different behaviour in different situations.

**“But how does it unleash Zalgo?” you may ask.**

Let us write some more logic to actually use this unpredictable function. Check it out below:

```js
function sleep(milliseconds) {  
      return new Promise(resolve => setTimeout(resolve, milliseconds));  
}  

let cache = {};

function getStringLength(text, callback) {

    if (cache[text]) {
        callback(cache[text])
    } else {
        setTimeout(function() {
            cache[text] = text.length;
            callback(text.length)
        }, 1000)
    }
}

function determineStringLength(text) {
    let listeners = []
    getStringLength(text, function(value) {
        listeners.forEach(function(listener) {
            listener(value)
        })
    })
    return {
        onDataReady: function(listener) {
            listeners.push(listener)
        }
    }
}

async function testLogic() {
    let text1 = determineStringLength("hello");
    text1.onDataReady(function(data) {
        console.log("Text1 Length of string: " + data)
    })

    await sleep(2000); 

    let text2 = determineStringLength("hello");
    text2.onDataReady(function(data) {
        console.log("Text2 Length of string: " + data)
    })
}

testLogic();
```

Pay special attention to the `determineStringLength()` function. It is a sort of wrapper around the `getStringLength()` function.

The `determineStringLength()` function creates a new object that acts as a notifier for the string length calculation. When the string length is calculated by the `getStringLength()` function, the listener functions registered within `determineStringLength()` get invoked.

To test this concept, you have the `testLogic()` function at the very end end. 

The test function calls `determineStringLength()` function twice for the same input string “hello”. Between the two calls, we pause the execution for a couple of seconds using the `sleep()` function. This is just to introduce a bit of time lag between the two calls.

Running the program provides the below result:

```bash
Text1 Length of string: 5
```

Only one statement is printed. The callback for the second operation never got invoked.

- For `text1`, the `getStringLength()` function behaves asynchronously since the data is **not** available in the cache. Therefore, the listener got registered and the output was printed.
- For `text2`, none of this happens. It gets created in an event loop cycle that already has the data available in the cache. Therefore, `getStringLength()` behaves synchronously and the callback that's passed to it gets called immediately. This in turn calls all the registered listeners synchronously. However, registration of new listener happens later and hence it is never invoked.

The root of this problem is the unpredictable nature of `getStringLength()` function. Instead of providing **consistency**, it increases the **unpredictability** of our program.

Such bugs can turn out to be extremely complicated to identify and reproduce in a real application. More often, they cause nasty bugs and **unleash Zalgo in our application**.

**So, how can we avoid all of this mess?**

## Use Deferred Execution

It might appear tricky but preventing such situations can be quite simple. **Just make sure the functions you are writing behave consistently in terms of synchronous vs asynchronous behaviour**.

For example, check the below code:

```js
function getStringLength(text, callback) {

    if (cache[text]) {
        process.nextTick(function() {
         callback(cache[text]);
        });
        //callback(cache[text])
    } else {
        setTimeout(function() {
            cache[text] = text.length;
            callback(text.length)
        }, 1000)
    }
}
```

Instead of directly triggering the callback, you can wrap it inside the `process.nextTick()`. This **defers** the execution of the function until the beginning of the next [event loop phase](https://progressivecoder.com/a-deeper-look-at-nodejs-event-loop-phases/).

---

Subtle reasons can cause nasty bugs. Unleashing Zalgo is one of them and hence, an interesting name was given to this situation.

As I mentioned in the beginning, the term was first used by **Isaac Z. Schlueter** who was also inspired by a post on Havoc’s Blog. Below are the links to those blogs:

[Designing APIs for Asynchrony](https://blog.izs.me/2013/08/designing-apis-for-asynchrony/)
[Callbacks, synchronous and asynchronous](https://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/)

You can check out those posts to get more background. I hope the example in this post was useful in understanding the issue on a more practical level.

If you liked this post, consider sharing it with friends and colleagues. You can also connect with me on other platforms.

[My Regular Newsletter](https://progressivecoder.substack.com/)

[Twitter](https://twitter.com/ProgressiveCod2)

[LinkedIn](https://www.linkedin.com/in/saurabh-dashora-b3601173/)

[Youtube](https://www.youtube.com/channel/UCcD92x9BQLvvm-8jtsg12kw)