🌀 JavaScript Event loop ♻️

Prevedeno/ukradeno sa https://www.youtube.com/watch?v=8aGhZQkoFbQ

JavaScript

  • Single threaded 🙂
  • Concurrent 🤔
  • Non-blocking 🙃
  • Asynchronous 😟

JavaScript engine

  • Call stack
  • Memory Heap

Runtime environment

  • Event Loop
  • Callback Queue
  • Web* API-s

Source

Single threaded

  • Jedan call stack
  • Naredbe se izvršavaju jedna po jedna

Call Stack

Radi na principu Last In, First Out (LIFO)

Demo

const multiply = (a, b) => a * b;
const square = a => multiply(a, a);
const printSquare = number => {
  const result = square(number);
  console.log(result);
};

printSquare(5);

Concurrency

  • Istovremenost - više događaja se izvršava u isto vrijeme (naizmjenično, paralelno)
  • JavaScript istovremenost = neke naredbe se izvršavaju van programskog toka
    • Ne idu na Call stack
  • Zašto nam je istovremenost potrebna?

Blokirajuće operacije 🚦

  • 🐌 Neučinkoviti i spori kôd
  • I/O (Input/Output) operacije
  • ⛔ Blokiraju izvođenje ostatka programa
  • Problem u preglednicima:
    • Onemogućavaju korištenje UI stranice
    • Loš User Experience (UX)
  • Primjer
Polje za unos: <input type="text" /><br /><br />
<button id="runSlowButton">Klikni za spori kôd</button>
<button id="storeButton">Spremi</button>
function verySlowFunction(miliseconds) {
  const startingTime = Date.now();
  while (true) {
    if (Date.now() - startingTime > miliseconds) {
      break;
    }
  }
}

document
  .getElementById('runSlowButton')
  .addEventListener('click', () => verySlowFunction(4000));
document
  .getElementById('storeButton')
  .addEventListener('click', () => console.log('Spremljeno'));

Non-blocking

  • Većina JavaScript I/O operacija ne blokira izvršavanje drugog JS kôda
  • I/O
    • Mrežni zahtjevi
    • Operacije nad diskom

Asynchronous

  • Asinkrone funkcije
    • izvršiti će se nakon što se završi neki drugi (non-blocking) zadatak
    • ne znamo kada će (i hoće li) taj zadatak biti završen
  • Na primjer, za AJAX poziv možemo odmah odrediti:
    • što će se dogoditi ako se završi uspješno
    • što će se dogoditi ako ne uspije

Kako JavaScript postiže istovremenost

  • Web APIs
  • Queue
  • Event loop

Web APIs

  • Nisu dio Javascripta, ali JS ih najčešće koristi
  • Primaju async callback funkcije i šalju ih kao poruke u Queue kad izvrše svoj zadatak

Queue

🐌🐢🐤🐇🐑🐑🐐🐄🐕🐈

  • Sadrži poruke koje je potrebno procesuriati
  • Svaka poruka je JS funkcija - Async callback
  • Poruke se procesuiraju po FIFO (First In, First Out) principu

Event loop 🔄

  • Promatra Call Stack i Queue
  • Poruke iz Queue-a stavlja na Call Stack ❗kad je Call Stack prazan❗
  • Dok god Call Stack nije prazan:
    • Event loop ne može procesuirati druge poruke iz Queue-a
    • Korisnik ne može koristiti UI preglednika

⚠️ Ne blokirajte Event Loop ⚠️

Async callback primjer

  • setTimeout funkcija

  • Što će sljedeći kôd ispisati u konzolu?

    console.log(1);
    setTimeout(function cb() {
      console.log(2);
    }, 0);
    console.log(3);
    

    Demo

Zagonetka 1️⃣

function verySlowFunction(miliseconds) {
  const start = Date.now();
  while (true) {
    if (Date.now() - start > miliseconds) {
      break;
    }
  }
}

const startingTime = Date.now();
setTimeout(function cb() {
  // Što će ovo ispisati?
  console.log(`Prošlo je ${Date.now() - startingTime} milisekundi`);
}, 1000);

verySlowFunction(2000);

Zagonetka 2️⃣

try {
  throw new Error('Ajme');
} catch (error) {
  console.log('Došlo je do greške: ', error);
}
try {
  setTimeout(() => {
    throw new Error('Jao');
  }, 0);
} catch (error) {
  console.log('Došlo je do greške: ', error);
}

Ako želimo uhvatiti grešku, moramo try-catch staviti unutar callback funkcije

setTimeout(() => {
  try {
    throw new Error('Jao');
  } catch (error) {
    console.log('Došlo je do greške: ', error);
  }
}, 0);

Zagonetka 3️⃣

function verySlowFunction(miliseconds) {
  const start = Date.now();
  while (true) {
    if (Date.now() - start > miliseconds) {
      break;
    }
  }
}

const startingTime = Date.now();

setTimeout(() => {
  verySlowFunction(1000);
  console.log(`1. Prošlo je ${Date.now() - startingTime} milisekundi`);
}, 0);

setTimeout(() => {
  console.log(`2. Prošlo je ${Date.now() - startingTime} milisekundi`);
}, 500);

Izvori

Što je JavaScript?

JavaScript building blocks

Call stack - govori nam gdje smo trenutno trenutno u kôdu

Memory Heap - gdje JavaScript sprema objekte, varijable

JS engine: V8 (Chrome), SpiderMonkey (Firefox)

JS Runtime environment: Blink (Chrome), Gecko (Firefox)

* Web API-s u preglednicima, C++ API-s u Node-u

Zanemarimo za sada Web Apis i Callback Queue kvadrate

Spori kôd je predvidiv i konzistentno spor, može se popraviti direktnom intervencijom programera

I/O operacije ovise o drugim faktorima na koje programer ne može uvijek utjecati

Kôd iz primjera sa prošlog slide-a

Ne možemo predvidjeti koliko dugo će trajati HTTP request, ne želimo da za to vrijeme ne radi UI

Dobro objašnjenje ovog koncepta se nalazi ovdje: http://www.michael-richardson.com/processes/rup_for_sqa/core.base_rup/guidances/concepts/concurrency_EE2E011A.html#Asynchronous%20vs.%20synchronous%20interaction

Non-blocking operacije se izvršavaju kroz WEB API i ne idu na call stack

Sve WEB API funkcije i metode primaju tzv. callback funkcije koje se šalju na tzv. Queue kad WEB API obavi svoj zadatak

Event loop određuje kako i kada će se poruke u Queue-u procesuriati

Činjenica da su dio preglednika, a ne JavaScripta, je ono što omogućava istovremenost: preglednici su multi-threaded

Što će se ispisati u konzolu?