# Asynchronous Javascript

## Synchronous execution

Basically, referring to a programming language, **Synchronous** means executing tasks **one at a time** in the order described by the source code. This means we cannot execute a certain operation until the previous one has finished.

However, in modern web applications, this kind of behavior would have a great impact on usability given the fact that certain tasks (such as communicating via network) might take an unreasonable time to complete, blocking the main thread of execution.

{% tabs %}
{% tab title="Synchronous  execution" %}

```javascript
let i = 0
function expensiveOperation() {
    //some code that needs a long time to execute
}

expensiveOperation() //taking 5 seconds
console.log("It's been a while")
```

{% endtab %}
{% endtabs %}

JavaScript engines consist of 3 main components:

* Global Memory
* Thread of execution
* Call Stack

Based on the order in the code, they push functions in the Call Stack, they execute them, while eventually reading or writing from the memory, one at a time, and then pop the function moving to the following one until the code is over.

## Asynchronous execution

Luckily, JavaScript Runtimes (such as browsers or NodeJS) have important additions to the core JavaScript engine, allowing an Asynchronous behavior.

Some of the features that aren't native in JavaScript, but available through a browser runtime are:

1. the console
2. rendering the HTML DOM
3. a timer
4. making requests via the network

Triggering their executions within the browser's background processes from JavaScript is possible with certain APIs, such as in the following examples:

1. `console.log()` triggers the browser to display the evaluation between the brackets
2. `document.getElementsById()` allows us to manipulate the way the DOM is being rendered
3. `setTimeout(expensiveOperation, 3000)` or `setInterval()` start a timer in a background browser process
4. `XMLHttpRequest()` and the more recent `fetch()` allows us to issue network requests and retrieve external answers

{% tabs %}
{% tab title="Asynchronous JavaScript example" %}

```javascript
const printData = data => console.log(data)
const printHello = () => console.log('Hello')
const expensive300msOperation = () => {}

setTimeout(printHello, 0) //printHello is a callback
//being a callback for a browser API, it gets pushed to the callback queue
const futureData = fetch('https://api.tvmaze.com/shows') //it returns a Promise
futureData //let's assume this takes 200ms
    .then(response => response.json()) //this gets pushed to the microtasks queue
    .then(printData) ////this gets pushed to the microtasks queue
    .catch(error => console.log('We encountered the following error: ' + error))

expensive300msOperation() //pushed to the Call Stack
console.log('Print me first!')
```

{% endtab %}
{% endtabs %}

## The order in which the runtime executes code

With the use of the Event Loop, which basically keeps asking if the main thread's call stack got emptied, the tasks in the MicroTask queue are being executed and popped until the queue gets empty, followed by the Callback queue tasks.

![](/files/-MLSJUp6nsqfNMvat8TZ)

By continuously feeding the Microtask queue, there is a risk (more likely when in NodeJS) to "starve" the Callback queue.

Asynchronicity is achieved through 3 methods

1. Callbacks
2. Promises
3. Async Await

## Promises

A `Promise` is an object representing the eventual completion or failure of an asynchronous operation.&#x20;

Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function. When a `Promise` is created, the object, stored in the Global Memory, contains some hidden properties and methods.

* The returned **value** if the Promise is fulfilled
* An array of callbacks which is immediately pushed to the MicroTask queue, once the value is returned, the first of those taking the value as the single argument
* An array of callbacks that are pushed to the MicroTask queue if the Promise chain is rejected.

These arrays of functions are basically chained functions. The resolve Array is created using the `.then()` method while the rejected array is created using the `.catch()` method.

```javascript
new Promise((resolve, reject) => {
    console.log('Initial');
    //issue a netowrk request

    setTimeout(()=> resolve('The response'), 2000);
})
.then((res) => {
    console.log(res)
    //assuming we're creating a new promise that rejects
    throw new Error('Something failed');
        
    console.log('Do this');
})
.catch(() => {
    console.error('Do that');
})
.then(() => {
    console.log('Do this, no matter what happened before');
});
```

## Async/await

The `async` keyword can be placed before a function, which will return a promise. The returned values of the function will be wrapped in a resolved promise automatically.

```javascript
async function f() {
  return 1;
}

f().then((res) => console.log(res)); // 1
```

&#x20;The keyword `await` makes JavaScript wait until that promise settles and returns its result.

```javascript
async function f() {

  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("done!"), 1000)
  });

  let result = await promise; // wait until the promise resolves (*)

  alert(result); // "done!"
}

f();
```

&#x20;`await` literally suspends the function execution until the promise settles, and then resumes it with the promise result.  It’s just a more elegant syntax of getting the promise result than `promise.then`, easier to read and write.

```javascript
//multiple async
const promisify = (item, delay) =>
  new Promise((resolve) =>
    setTimeout(() =>
      resolve(item), delay));

const a = () => promisify('a', 100);
const b = () => promisify('b', 5000);
const c = () => promisify('c', 3000);

async function parallel() {
  const promises = [a(), b(), c()];
  const [output1, output2, output3] = await Promise.all(promises);
  return `prallel is done: ${output1} ${output2} ${output3}`
}

async function race() {
  const promises = [a(), b(), c()];
  const output1 = await Promise.race(promises);
  return `race is done: ${output1}`;
}

async function sequence() {
  const output1 = await a();
  const output2 = await b();
  const output3 = await c();
  return `sequence is done ${output1} ${output2} ${output3}`
}

sequence().then(console.log)
parallel().then(console.log)
race().then(console.log)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mihai-gheorghe.gitbook.io/tic/asynchronous-javascript.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
