1 of 19

Asynchronisme et Event Loop en JavaScript

Benjamin Cavy

2 of 19

Benjamin Cavy

Dev à la MAIF (on recrute ! )

https://maif.github.io/

@benjamin_cavy

#JSC2019

@Speaker

3 of 19

Asynchronisme

  • Exécution de code différée

  • Permet de gérer les actions normalement bloquantes

  • Très utile quand on a qu’un thread !

@benjamin_cavy

#JSC2019

@Speaker

4 of 19

const printSquareSum = (a, b) =>

console.log(square(sum(a, b)));

const sum = (a, b) => a + b;

const square = n => n * n;

printSquareSum(1, 2);

Code (main)

Stack

main();

const printSquareSum = ...

const sum = ...

const square = ...

printSquareSum(1, 2);

sum(1, 2);

square(3);

console.log(9);

9

Console

@benjamin_cavy

#JSC2019

@Speaker

5 of 19

console.log("Start");

setTimeout(

() => console.log("Hello Tours"), 100

);

console.log("End");

console.log("Start");

Start

setTimeout();

console.log("End");

End

console.log("Hello Tours");

Hello Tours

Stack

main();

Console

Code (main)

@benjamin_cavy

#JSC2019

@Speaker

6 of 19

console.log("Start");

setTimeout(

() => console.log("timeout 1"), 100

);

setTimeout(

() => console.log("timeout 2"), 100

);

console.log("End");

Stack

main();

console.log("Start");

console.log("End");

Start

End

timeout 1

Console

console.log("timeout 1")

console.log(

"timeout 1"

);

Task Queue

console.log("timeout 1")

Browser APIs

console.log("timeout 2")

console.log(

"timeout 2"

);

console.log("timeout 2")

setTimeout(

() => console.log("timeout 2"),

100

);

setTimeout(

() => console.log("timeout 1"),

100

);

console.log(

"timeout 2"

);

timeout 2

Code (main)

@benjamin_cavy

#JSC2019

@Speaker

7 of 19

Task queue

  • Peuplée par le navigateur

  • Task : callback (timeout, clic handler, …)

  • Exécutées par l’event loop en mode FIFO ...

  • … uniquement quand la stack est vide

@benjamin_cavy

#JSC2019

@Speaker

8 of 19

Tasks queues

  • Il peut y avoir plusieurs task queues !

  • C’est le cas dans tous les principaux navigateurs

  • Permet au navigateur de prioriser certaines tâches

  • Même source => même queue

@benjamin_cavy

#JSC2019

@Speaker

9 of 19

Render queue

  • Alimentée à partir des callbacks de requestAnimationFrame

  • Consommée par l’event loop avant le repaint du navigateur

  • … qui a lieu uniquement si la stack est vide

  • Les éléments présents initialement sont tous exécutés

  • Prioritaire sur la task queue (avant le repaint)

@benjamin_cavy

#JSC2019

@Speaker

10 of 19

for (let i = 0; i < 1000; i++) {

setTimeout(() => {

console.log("start");

for (let j = 0; j < 100; j++) {}

console.log("stop");

});

}

requestAnimationFrame(

() => console.log("RAF"));

Code (main)

Stack

main();

start

stop

Console

Task Queue

Render Queue

console.log("start");

for (

let j = 0;

j < 100;

j++

) {}

console.log("stop");

console.log("RAF");

setTimeout();

requestAnimationFrame();

console.log("start");

for (

let j = 0;

j < 100;

j++

) {}

console.log("stop");

start

stop

console.log("RAF");

start

stop

RAF

start

stop

x1000

x999

x998

@benjamin_cavy

#JSC2019

@Speaker

11 of 19

for (let i = 0; i < 1000; i++) {/* … */}

const recursiveRAF = f => {

requestAnimationFrame(() => {

f();

recursiveRAF(f);

});

};

recursiveRAF(() => console.log("RAF_1"));

recursiveRAF(() => console.log("RAF_2"));

Code (main)

Stack

start

stop

Console

Task Queue

Render Queue

console.log("start");

for (

let j = 0;

j < 100;

j++

) {}

console.log("stop");

f(); // RAF_1

recursiveRAF(f);

console.log("start");

for (

let j = 0;

j < 100;

j++

) {}

console.log("stop");

x1000

x999

f(); // RAF_2

recursiveRAF(f);

f(); // RAF_1

console.log("RAF_1");

recursiveRAF(f);

f(); // RAF_1

recursiveRAF(f);

f(); // RAF_2

recursiveRAF(f);

f(); // RAF_1

recursiveRAF(f);

RAF_1

recursiveRAF(f);

f(); // RAF_2

console.log("RAF_2");

f(); // RAF_2

recursiveRAF(f);

RAF_2

start

stop

requestAnimationFrame();

@benjamin_cavy

#JSC2019

@Speaker

12 of 19

Micro tasks

  • Prioritaires sur les tasks

  • Crées par les Promises, MutationObserver, …

  • Une fois lancée, leur exécution est ininterrompue jusqu’à épuisement de la queue

@benjamin_cavy

#JSC2019

@Speaker

13 of 19

setTimeout(() =>

console.log("task")

);

Promise.resolve()

.then(() =>

console.log("microtask")

);

Code (main)

Stack

Console

Task Queue

Render Queue

MicroTask Queue

console.log("task")

console.log("microtask")

console.log("microtask")

microtask

console.log("task")

task

@benjamin_cavy

#JSC2019

@Speaker

14 of 19

const p = Promise.resolve();

for (let i = 0; i < 100000; i++) {

p.then(() =>

console.log("promise"));

}

setTimeout(() =>

console.log("timeout"));

requestAnimationFrame(() =>

console.log("RAF"));

Code (main)

Stack

Console

Task Queue

Render Queue

MicroTask Queue

console.log("promise")

x100000

console.log("RAF")

console.log("timeout")

console.log("promise")

x99999

promise

x2

x99998

x10000

console.log("RAF")

RAF

console.log("timeout")

timeout

@benjamin_cavy

#JSC2019

@Speaker

15 of 19

Résumé

  • 3 types de queues
    • Tasks
      • Exécution unitaire
      • Plusieurs queues (même origine = même queue)
    • Render
      • Alimentée par les callbacks de requestAnimationFrame
      • Exécution avant le repaint (idéalement 60x / sec)
      • Exécution de tous les éléments présents initialements
      • Prioritaire sur les tasks (avant le repaint)
    • MicroTasks
      • Consommation de la queue jusqu’à épuisement
      • Priorité la plus haute

@benjamin_cavy

#JSC2019

@Speaker

16 of 19

Conclusion

  • 3 types de queues

  • Dépilées uniquement quand la stack est vide !

  • Asynchrone !== non bloquant

  • Attention à la compatibilité Internet Explorer / Edge

@benjamin_cavy

#JSC2019

@Speaker

17 of 19

Pointeurs

  • Les specs de l’event loop

@benjamin_cavy

#JSC2019

@Speaker

18 of 19

Merci de votre attention !

@benjamin_cavy

#JSC2019

@Speaker

19 of 19

Bonus : requestIdleCallback

  • Méthode dédiée aux tâches non urgentes

requestIdleCallback(() => console.log("idle"));

  • Pas dans IE / Edge
  • Très faible priorité
  • Durée maximale avant exécution
  • Reçoit un objet de la forme

{ didTimeout: boolean, timeRemaining: function }

@benjamin_cavy

#JSC2019

@Speaker