📕
TIC
  • Tehnologii ale InformaÅ£iei ÅŸi ComunicaÅ£iilor (TIC)
  • Basic web principles
    • How web pages work
    • The pillars of a web page
    • Extra: Getting Started with GitHub
  • Basic HTML
    • Description and Basic Syntax
    • Extra resources
  • Basic CSS
    • Description and Basic Syntax
    • Advanced Positioning
    • Extra Resources
  • Basic Javascript
    • Description and basic syntax
    • The Document Object Model
    • Extra Resources
    • Basic assignment
    • The Color Game
  • Advanced Javascript
    • Runtime Engine, Callstack, Scope
    • ES6
  • Advanced Javascript 2
  • Programming paradigms
  • OOP Javascript
  • Functional Programming
  • OOP vs. Functional Programming
  • Asynchronous Javascript
  • Backend Javascript
    • NodeJS
    • ExpressJS
    • REST APIs
    • Authentication and Authorization
  • Firebase
    • NoSQL Databases
    • Database as a Service
    • Google Cloud Firestore
    • CRUD operations
    • Securing your database
  • Basic VueJS
  • Agenda: VueJS and Frontend Frameworks
  • Single Page Applications
  • VueJS basic syntax
  • Vue Components
  • Advanced VueJS
  • Advanced apps with Vue CLI
  • Vue Router
  • SPA State Management - Vuex
  • Composition API
  • Evaluation
    • Final Individual assignment
Powered by GitBook
On this page
  • Synchronous execution
  • Asynchronous execution
  • The order in which the runtime executes code
  • Promises
  • Async/await

Was this helpful?

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.

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")

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

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!')

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.

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.

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.

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.

async function f() {
  return 1;
}

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

The keyword await makes JavaScript wait until that promise settles and returns its result.

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();

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.

//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)
PreviousOOP vs. Functional ProgrammingNextNodeJS

Last updated 4 years ago

Was this helpful?