Zenika Academy – Coder l'asynchrone en JavaScript – By Matthieu Lux



Zenika Academy – Coder l'asynchrone en JavaScript – By Matthieu Lux

0 1


talk-async-js

Lightning talk about asynchronous code in JS / ES6 / ES6+

On Github Swiip / talk-async-js

Zenika Academy

Coder l'asynchrone en JavaScript

By Matthieu Lux

Coder l'asynchrone n'est pas facile

- Ce n'est pas transparent, quoi qu'on fasse, ça change la structure du code - Et ça peut vite être le bazar - Qui n'a pas déjà eu des comportements aléatoires en JS, la plupart du temps, cela vient d'un asynchronisme mal controlé

Synchrone

const oAuthResponse = syncRequest(oAuthOptions());
const token = JSON.parse(oAuthResponse.body).access_token;

const searchResponse = syncRequest(searchOptions(token));
const search = JSON.parse(searchResponse.body);

search.statuses.forEach(status => console.log(status.text));
- C'est un extrait de code, le code intégral est sur Github - Il s'agit d'une recherche sur l'API Twitter - Il faut gérer l'authentification avant la requête elle même - Le = ici n'est pas anodin mais n'est possible qu'en synchrone

Simple mais inutilisable et contre performant

- Le JavaScript est asynchrone, toute l'appli s'arrête pendant les requêtes

On utilise alors les callbacks pour passer à l'asynchrone

- C'est le cas de l'API native de Node encore aujourd'hui - Ainsi que la plupart des implémentations de requête ajax y compris jQuery

Callbacks

callbackRequest(oAuthOptions(), (error, response, body) => {
  const token = JSON.parse(body).access_token;
  callbackRequest(searchOptions(token), (error, response, body) => {
    const search = JSON.parse(body);
    search.statuses.forEach(status => console.log(status.text));
  });
});
- Plus valeurs de retour - Rupture dans le sens du lecture du code - A la ligne du bas du code, on a pas la valeur de retour - On voit apparaitre une indentation qui sera rapidement difficile à maîtriser - Cela s'appelle le Callback Hell

L'utilisation importante des callbacks crée un code complexe

- Et on aime pas les codes complexe - C'est difficile à maintenir et c'est générateur de bug

Comment avoir du code asynchrone maintenable ?

Les promesses

- D'abord sous forme de librairie - Intégrée aujourd'hui à la norme ES6 - Un objet JavaScript manipulable mais qui ne représente pas la donnée - On peut ensuite écouter son évènement de succès ou d'échec - Cela permet de s'échanger l'objet en attente sans savoir ce qu'un autre code en fera

Promesses

promiseRequest(oAuthOptions()).then(response => {
  const token = JSON.parse(response).access_token;
  return promiseRequest(searchOptions(token));
}).then(response => {
  const search = JSON.parse(response);
  search.statuses.forEach(status => console.log(status.text));
});
- C'est grâce au chaînage que le code se simplifie - Plutôt que d'imbrique on retourne la seconde promesse - Cela permet de rester au même niveau d'indentation

On peut aller encore plus loin

ES6 incoming

- Mais gardez les promesses dans un coin de votre tête

Generators

function *foo() {
  yield 1;
  yield 2;
  yield 3;
}

var it = foo();
console.log( it.next() ); // { value:1, done:false }
console.log( it.next() ); // { value:2, done:false }
console.log( it.next() ); // { value:3, done:false }
console.log( it.next() ); // { value: undefined, done: true }
- ES6 propose la syntaxe function * avec les yield qu'on appele generator - Contrairement à beaucoup de nouveauté d'ES6, ce n'est pas du sucre syntaxique - C'est une fonction qui ne s'executera pas entierement, elle bloquera sur les yield - Le next qu'on voit en bas permet de relancer l'execution

Et si on yield une promesse ?

- Si on yield une promesse, il faut un code qui attends la fin de la promesse avant de continuer - Ce code est générique et peut être fait une fois pour toute - C'est le cas notamment de la librairie co

Generators

import co from 'co';

co(function *() {
  const oAuthResponse = yield promiseRequest(oAuthOptions());
  const token = JSON.parse(oAuthResponse).access_token;
  const searchResponse = yield promiseRequest(searchOptions(token));
  const search = JSON.parse(searchResponse);
  search.statuses.forEach(status => console.log(status.text));
});
- On revient à une syntaxe qui totalement linéaire - Attention, c'est toujours asynchrone, ça n'implique pas de changement en terme de perf - Les promesses sont au coeur du mécanisme, ce n'est pas un concept à oublier

Peut-on aller encore plus loin ?

ES7 incoming

- Attention, on passe dans des specs très avancées mais non validée - L'idée est de se dire que quitte à avoir les generateurs pour pouvoir faire de l'asynchrone - Autant répondre au problème jusqu'au bout - Le mécanisme générateur + promesse est intégré dans les async functions

Async

async function start() {
  const oAuthResponse = await promiseRequest(oAuthOptions());
  const token = JSON.parse(oAuthResponse).access_token;
  const searchResponse = await promiseRequest(searchOptions(token));
  const search = JSON.parse(searchResponse);
  search.statuses.forEach(status => console.log(status.text));
}

start();
- On revient à une syntaxe qui totalement linéaire - Attention, c'est toujours asynchrone, ça n'implique pas de changement en terme de perf - Les promesses sont au coeur du mécanisme, ce n'est pas un concept à oublier

Coder l'asynchrone en JavaScript

Slidesswiip.github.com/talk-async-js

Examplesgithub.com/Swiip/talk-async-js/tree/master/examples

Matthieu Lux (@Swiip)

Zenika Academy Coder l'asynchrone en JavaScript By Matthieu Lux