JavaScript Enjoys Your Tears
Tags: javascript, webdev, asynchronous, synchronous
Published: 2019-07-10 14:31:52

---
title: JavaScript Enjoys Your Tears
published: true
description: JavaScript is Single-Threaded? YES. But I can do things Asynchronously in it? YES. This is a talk I've given a few times on the Synchronous and Asynchronous behavior of JavaScript.
tags: JavaScript, WebDev, Asynchronous, Synchronous
series: JavaScript Details
cover_image: https://thepracticaldev.s3.amazonaws.com/i/huic7xuksqu8i2yaeg5c.png
---

Video:
* [JavaScript and Friends Presentation: 17 Mar 2020](https://www.youtube.com/watch?v=G3edxhIZPbw)

## JavaScript Enjoys Your Tears

This is a talk I've given a few times on the Synchronous and Asynchronous behavior of JavaScript.  The actual talk is a semi-***live-coded*** talk.  It has made me nervous leaving the actual code out there for people to play with after the talk, so this post was created.

[Single-Threaded and Asynchronous JavaScript?](https://sessionize.com/s/bob-fornal/single-threaded_and_asynchronous_ja/22652)

## Spoiler ...

At it's core, JavaScript is a synchronous, blocking, single-threaded language. This means that only one thing can happen at a time.

When people say that JavaScript is an asynchronous language, what they mean is that you can manipulate JavaScript to behave asynchronously.

Boundaries of Asynchronicity

* User Interactions
* Network IO
* Disk IO
* Inter-Process Communications
* Timers

## Definitions

**Parallelism:** multi-thread processing and scheduling (same time).
**Asynchronous:** single-thread and event loop.

### Managed by ...

**Concurrency:** higher-level tasks that can occur within the same time frame.
**Contention:** multiple things need to occur at the same instant.

## JavaScript Engine Details

These are not a part of the JS Engine; they are included in Browser or NodeJS runtime environment:

* setTimeout
* Event Loop
* Web APIs
* Message Queue
* Task Queue

Here's a great visualization of "JavaScript's call stack/event loop/callback queue (and how they) interact with each other": [Loupe](http://latentflip.com/loupe/).

***setTimeout*** (4ms delay); see this article on [MDN setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Reasons_for_delays_longer_than_specified).

> In modern browsers, setTimeout()/setInterval() calls are throttled to a minimum of once every 4ms when successive calls are triggered due to callback nesting (where the nesting level is at least a certain depth), or after certain number of successive intervals. - MDN

## Callback Hell

**"Pyramid of Doom":**
* Symptom of the problems, not the real issue.

**Inversion of Control:**
* TRUST POINT when callback is passed ... does it behave as expected.

**REASONable:**
* Temporal Dependency requires nesting; non-linear thinking.

### Considerations ...

**Coordination of Data:**
* At higher level to track data across callbacks.

**Split Callbacks:**
* Separating out success and errors.

**Error First:**
* Same trust issues as Split Callback.

**Promise Hell ...**
* Flow control with bad style.
* Nested promises rather than vertical chaining.

## What will the following code do?

```javascript
export class ThoughtExercise {
  _time = 10000;
  _wrapper = null
	
  constructor() {};

  changeColor = () => {
    this._wrapper = document.getElementById('thought-wrapper');
    this._wrapper.style.backgroundColor = 'red';		
  };
  changeLayout = () => {
    let p = document.createElement('p');
    p.setAttribute('id', 'thought-run');
    p.innerText = 'Thought Exercise ...';
    this._wrapper.appendChild(p);
  };
  wait = () => {
    const start = Date.now();
    while(Date.now() < start + this._time) {};
  };
  event = () => {
    this.changeColor();
    this.changeLayout();
    this.wait();
  };
	
  start = () => {
    const button = document.getElementById('thought-button');
    button.classList.remove('hide');
    button.addEventListener('click', this.event);
  };
}

```

### Answer

This code essentially wires up a button, that when click fires the ***changeColor***, ***changeLayout***, and ***wait*** functions.

When the button is clicked, this code will be thread-locked until ***this._time*** has passed. The background color will not be changed until 10,000ms has passed.

## Non-Asynchronous Code

Given the following code ...

```javascript

export class NonAsynchronous {
  _numbers = [1, 2, 3];
	
  constructor() {};
	
  forEachSync = (items, callback) => {
    for (const item of items) {
      callback(item);
    }
  };
	
  forEachAsync = (items, callback) => {
    for (const item of items) {
      setTimeout(() => {
        callback(item);
      }, 0, item);
    }
  };
	
  runSync = () => {
    console.log('The Start');
    this.forEachSync(this._numbers, (number) => {
      console.log(number * 2);
    });
    console.log('The End');
  };

  runAsync = () => {
    console.log('The Start');
    this.forEachAsync(this._numbers, (number) => {
      console.log(number * 2);
    });
    console.log('The End');
  };
	
  start = (async = false) => {
    if (!async) {
      this.runSync();
    } else {
      this.runAsync();
    }
  }	
}

```

Basically, there are two different ***start**s that can occur here: Non-Asynchronous and Asynchronous; each has a run option (***runSync*** and ***runAsync*** respectively).  Each run as an associated forEach functionality.

When ***this.runSync*** fires, we should see the following in the console ...

![](https://thepracticaldev.s3.amazonaws.com/i/tdjhysr76yglfmwdtwun.png)

When ***this.runAsync fires, we should see the following ...

![](https://thepracticaldev.s3.amazonaws.com/i/pemhhhe95e7gtwpn4ldf.png)

Note the differences here. When running synchronously, everything occurs in the order we expect. When running asynchronously, the numbers consoled show up outside the flow of normal JavaScript execution.

## Simple Network (simulation)

**Callbacks**
* Seams that rip across the application; some bootstrapping may not be complete when needed.

**Issues dealing with errors in logic.**
* Difficult to understand: Nesting, never called, called repeatedly, called synchronously (blocking)

Simple network, simulated by 10 second timeout.

```javascript
export class SimpleNetwork {
  _time = 10000;
	
  constructor() {};
	
  networkRequest = () => {
    setTimeout(() => {
      console.log(`Async Code after ${this._time}ms.`);
    }, this._time);
  };
  start = () => {
    console.log('The Start');
    this.networkRequest();
    console.log('The End');
  };
};
```

Within this code, we are basically simulating a network request; using setTimeout to provide a ten-second delay. We should see that the network request completes outside the flow of normal JavaScript execution ...

![](https://thepracticaldev.s3.amazonaws.com/i/hlqbjcsnt17ferzg5yma.png)

What is seen when this code is run is that the last line actually displays after a ten-second delay.

## Complex Network (simulation)

Complex network, simulated by nested timeouts.

```javascript
export class ComplexNetwork {
  _time = 0;
	
  constructor() {};
	
  first = () => {
    setTimeout(() => {
      console.log('2');
      this.second();
      console.log('4');
    }, this._time);
  };

  second = () => {
    setTimeout(() => {
      console.log('3');
    }, this._time);
  };

  start = () => {
    console.log('1');
    this.first();
    console.log('5');
  };
}
```

What we should expect when examining this simulation is that the order should be: 1, 5, 2, 4, 3 (because of the order of the setTimeout / asynchronous operations) ...

![](https://thepracticaldev.s3.amazonaws.com/i/qdlysrbf7gkjw3w97xtu.png)

## ES2015 Simple Promise

Completion and Error Events handle inversion of control issue.

**Promise Trust**
1. Only resolve once
2. Either success or error
3. Messages passed/kept
4. Exceptions become errors
5. Immutable once resolved

**Simple async with timeout and promise. Issues:**
* Passing values
* Nested syntax
* Handling failure ...

Also called: Promises, future, deferred.

```javascript
export class SimplePromise {
  _time = 0;
  
  constructor() {}
  
  timeout = () => {
    setTimeout(() => {
      console.log('setTimeout Fired');
    }, this._time);    
  };
  
  promise = () => {
    new Promise((resolve, reject) => {
      resolve('Resolved');
    })
    .then(res => console.log(res))
    .catch(err => console.log(err));    
  };
  
  start = () => {
    console.log('The Start');
    
    this.timeout();
    this.promise();
    
    console.log('The End');
  };
}
```

The code here ***should*** fire 'The Start', then trigger the timeout and promise functions and finally 'The End'.  The order of operations in this case should be 'The Start' and 'The End' should be displayed. Since the promise is immediately resolved and has nothing takes it outside the flow of normal JavaScript execution, it should display next. And finally, the timeout function will display.

![](https://thepracticaldev.s3.amazonaws.com/i/0kclrvxewi52sydqs87a.png)

## ES2015 Complex Promise

Complex async with timeout and chained promises.
* Modular and readable, but slightly wonky.

```javascript
export class ComplexPromise {
  _time = 0;
  
  constructor() {}
  
  timeout = () => {
    setTimeout(() => {
      console.log('setTimeout Fired');
    }, this._time);    
  };
  
  promise1 = () => {
    return new Promise((resolve, reject) => {
      resolve('Resolved 1');
    })
    .then(res => console.log(res))
    .catch(err => console.log(err));    
  };
  
  promise2 = () => {
    return new Promise((resolve, reject) => {
      resolve('Resolved 2');
    })
    .then(res => {
      console.log(res);
      this.promise3();
    })
    .catch(err => console.log(err));    
  };
  
  promise3 = () => {
    new Promise((resolve, reject) => {
      resolve('Resolved 3');
    })
    .then(res => console.log(res))
    .catch(err => console.log(err));    
  };
  
  start = () => {
    console.log('The Start');
    
    this.timeout();
    this.promise1();
    this.promise2();
    
    console.log('The End');
  };
};
```

Here, we see something similar to the simple promise. The biggest difference is the chained promise 2 and 3.  Here, we should see the same as the simple promise example with all the promises completing before the timeout runs ...

![](https://thepracticaldev.s3.amazonaws.com/i/zbmchr78qq9ebweq68fw.png)

## Generator Throttling

Cooperative Concurrency versus Preemptive Concurrency.
* Syntactic form of a state-machine.
* About solving the "reasoning about" issue.
* Allow non run-to-completion behavior. Localized blocking only.
* Generators return an iterator.

```javascript
export function * throttle(func, time) {
  let timerID = null;
  function throttled(arg) {
    clearTimeout(timerID);
    timerID = setTimeout(func.bind(window, arg), time);
  }
  while(true) throttled(yield);
}

export class GeneratorThrottle {
  
  constructor() {};
  
  start = () => {
    thr = throttle(console.log, 3000);
    thr.next('');
  };
};
```

Here, when the generator is started, ***thr*** is initialized to run ***console.log*** after three-seconds.

![](https://thepracticaldev.s3.amazonaws.com/i/yie7w0tsbgbnifupu501.png)

Now, we can see that after initialization, the next function was called three times ... but only one console was fired at the end of the three-second window.

## User Interaction

```javascript
export class UserInteraction {
  
  constructor() {};
  
  dragStart = (event) => {
    event.dataTransfer.setData('text/plain', event.target.id);
    console.log('drag start', event);
  };
  
  dragOver = (event) => {
    event.preventDefault();
    event.dataTransfer.dropEffect = 'move';
    console.log({ x: event.pageX, y: event.pageY });
  };
  
  drop = (event) => {
    const id = event.dataTransfer.getData('text');
    console.log('drop', id);
    const element = document.getElementById('drag');
    event.target.appendChild(element);
  };
}
```

Basically, this code allows us to see that the drag and drop events do not thread lock JavaScript.

## Event Listeners

Event Listeners are Synchronous (not async)

```javascript
export class EventListeners {
  _btn = null;
  _time = 100;
  
  constructor() {};
  
  output = (content) => {
    console.log(content);
  };
  
  setupListeners = () => {
    this._btn.addEventListener('click', this.output.bind(null, 'Click Handler 1'));
    this._btn.addEventListener('click', this.output.bind(null,'Click Handler 2'));
  };

  triggerListeners = () => {
    setTimeout(() => {
      console.log('The Start');
      this._btn.click();
      console.log('The End');
    }, this._time);
  };
  
  start = () => {
    this._btn = document.getElementById('event-listener-link');
    this.setupListeners();
    this.triggerListeners();
  };
}
```

We should see that the click events both fire, in order ...

![](https://thepracticaldev.s3.amazonaws.com/i/yc6cq949bb56f2l4ik4j.png)

##  Web Workers

**Number of Workers**
* The number varies from browser to browser. Optimal seems to be around 20. See (StackOverflow on Number of Web Workers Limit)[https://stackoverflow.com/questions/13574158/number-of-web-workers-limit].

Here is ***crunch-numbers.js***, used as a web worker ...

```javascript
onmessage = function() {
  for (let step = 0, len = 10; step <= len; step++) {
    postMessage(step * 10);
    const start = Date.now();
    while (Date.now() < start + 1000) {};
  }  
}
```

This is the code that uses (or not) the web worker code ...

```javascript
export class WebWorkers {
  _worker = new Worker('scripts/presentation/crunch-numbers.js');
  _inlineProgress = null;
  _workerProgress = null;
  
  contructor() {};

  crunchNumbersInline = (callback) => {
    for (let step = 0, len = 10; step <= len; step++) {
      callback(step * 10);
      const start = Date.now();
      while (Date.now() < start + 1000) {};
    }
  };
  
  displayPercentInline = (percent) => {
    console.log(`inline percent: ${percent}`);
    this._inlineProgress.value = percent;
  };
  
  displayPercent = (message) => {
    console.log(`web-worker percent: ${message.data}`);
    this._workerProgress.value = message.data;
  }
  
  runSync = () => {
    this._inlineProgress = document.getElementById('inline-worker');
    this.crunchNumbersInline(this.displayPercentInline);
  };
  
  runAsync = () => {
    this._workerProgress = document.getElementById('web-worker');
    this._worker.postMessage('start');
    this._worker.onmessage = this.displayPercent;
  };
  
  start = (async = false) => {
    if (!async) {
      this.runSync();
    } else {
      this.runAsync();
    }
  };
}
```

What happens here is difficult to see without the associated HTML page running.  What this shows is that the inline process gets thread-locked and the percent display does nothing until the time expires, then it shows 100% in a single "jump."

In the case of the web-worker, each 10% increment is displayed properly without JavaScript getting thread-locked.

## Load Timing

Original Content (expect this to change via code)
Changed Content Correctly (via code)

The display above it the result of ...

```javascript
class LoadTiming {
  _time = 10000;
  
  constructor() {};
  
  loadSync = () => {
    const element = document.getElementById('first-timing');
    if (element) {
      element.innerHTML = 'Changed Content Correctly (via code)';
    }
  };
  
  loadAsync = () => {
    setTimeout(() => {
      const element = document.getElementById('second-timing');
      if (element) {
        element.innerHTML = 'Changed Content Correctly (via code)';
      }
    }, this._time);
  };
  
  start = () => {
    this.loadSync();
    this.loadAsync();
  };
}

const code11 = new LoadTiming();
code11.start();
```

As you can see, the code above loads the Synchronous and Asynchronous code immediately.  Since the JavaScript here is loaded in the HEAD content, it runs before the BODY content (DOM) is in place and the Synchronous functionality fails silently on the ***getElementById***.  The Asynchronous version has enough of a delay in place to ensure the DOM is ready and it can update the content as seen in the code.

## Set Timeout Timer

In this code, we want to look at how long the ***setTimeout*** delay actually is.

How long is a setTimeout delay?
* ***(Careful with delay tested ... 1000 iterations)***

```javascript
export class SetTimeoutTimer {
  _repetitions = 0;
  _totalRepetitions = 1000;
  _delay = 0;
  
  _totalActualDelay = 0;

  constructor() {};
  
  getActualDelay = () => {
    return this._totalActualDelay / this._totalRepetitions;
  };
  
  iterate = () => {
    let start = new Date();
    setTimeout(() => {
      this._totalActualDelay += new Date() - start;
      this.testDelay();
    }, this._delay);
  };
  
  testDelay = () => {
    if (this._repetitions++ > this._totalRepetitions) {
      console.log(`Requested Delay: ${this._delay}, Acual Average Delay: ${this.getActualDelay()}`);
      return;
    }
    this.iterate();
  };
  
  start = (delay = 0) => {
    this._delay = delay;
    this._repetitions = 0;
    this._totalActualDelay = 0;
    this.testDelay();
  };
}
```

The answer here is not 42.  It is generally 4ms as a default for ***setTimeout***. I've seen variation on different machines and browsers from 4ms to around 8ms ... also, as you can see here it is actually not a round number (does not fire AT 4ms, just some time after that when JavaScript can handle it).

![](https://thepracticaldev.s3.amazonaws.com/i/9p6is975o3opnuzwib5d.png)

## ES2017 Async/Await

* Expands on use of Promises.
* Writing asynchronous code that looks and feels synchronous.
* Cleans up the syntax, making it more readable.

```javascript
export class AsyncAwait {
  _time = 2000;
  _resolve = true;
  _success = `Doing something here ... after ${this._time}ms.`;
  _fail = `Failed here ... after ${this._time}ms.`;

  constructor() {};
  
  asyncProcess = () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => { (this._resolve === true) ? resolve(this._success) : reject(this._fail); }, this._time);
    });
  };
  
  asyncAwait = async () => {
    try {
      console.log(await this.asyncProcess());
    } catch (error) {
      console.log(error);
    }
  };
  
  start = (resolveState = true) => {
    this._resolve = resolveState;
    console.log('The Start');
    this.asyncAwait();
    console.log('The End');
  };
}
```

Basically, when this code starts it runs an async/await version of the promise. I was actually asked in a talk how it handled REJECT from the promise and I had to look it up (try/catch block).

Here is the async/await that resolves correctly ...

![](https://thepracticaldev.s3.amazonaws.com/i/eh9jolevvxj4695clbsc.png)

... and the same code with reject ...

![](https://thepracticaldev.s3.amazonaws.com/i/apsxharl6bwu8vukntbb.png)

## Summary

We've examined:

* Sync and Async code using callbacks. ... debugging
* ES2015 Promise(s) Chains.
* Generators (throttling)
* User Interaction.
* Event Listeners (synchronous).
* Web Workers.
* Load Timing.
* ES2017 Async/Await.

## Conclusion

All of this comes from a talk I've given a few times on the Synchronous and Asynchronous behavior of JavaScript.  The actual talk is a semi-***live-coded*** talk.  It has made me nervous leaving the actual code out there for people to play with after the talk, so this post was created.

[Single-Threaded and Asynchronous JavaScript?](https://sessionize.com/s/bob-fornal/single-threaded_and_asynchronous_ja/22652)