web-dev-qa-db-fra.com

Programmation asynchrone JavaScript: promesses vs générateurs

Les promesses et les générateurs vous permettent d'écrire du code asynchrone. Je ne comprends pas pourquoi ces deux mécanismes sont introduits dans le script ECMA 6. Quand est-il préférable d'utiliser les promesses et quand les générateurs?

49
sribin

Il n'y a pas d'opposition entre ces deux techniques. Ils coexistent ensemble se complétant bien. Les promesses vous donnent la possibilité d'obtenir le résultat d'une opération asynchrone qui n'est pas encore disponible. Il résout Pyramid of Doom problème. Donc au lieu de:

function ourImportantFunction(callback) {
  //... some code 1
  task1(function(val1) {
    //... some code 2
    task2(val1, function(val2) {
      //... some code 3
      task3(val2, callback);
    });
  });
}

tu peux écrire:

function ourImportantFunction() {
  return Promise.resolve()
    .then(function() {
        //... some code 1
        return task1(val3)
    })
    .then(function(val2) {
        //... some code 2
        return task2(val2)
    })
    .then(function(val2) {
        //... some code 3
        return task3(val2);
    });
}

ourImportantFunction().then(callback);

Mais même avec des promesses, vous devez écrire du code de manière asynchrone - vous devez toujours passer des rappels aux fonctions. L'écriture de code asynchrone est beaucoup plus difficile que synchrone. Même avec des promesses lorsque le code est énorme, il devient difficile de voir l'algorithme (enfin, c'est très subjectif, quelqu'un peut le contredire. Mais pour la majorité des programmeurs, je pense que c'est vrai). Nous voulons donc écrire du code asynchrone de manière synchrone. C'est là que les générateurs viennent nous aider. Ainsi, au lieu du code ci-dessus, vous pouvez écrire:

var ourImportantFunction = spawn(function*() {
    //... some code 1
    var val1 = yield task1();
    //... some code 2
    var val2 = yield task2(val1);
    //... some code 3
    var val3 = yield task3(val2);

    return val3;
});

ourImportantFunction().then(callback);

où la réalisation la plus simple possible spawn peut être quelque chose comme:

function spawn(generator) {
  return function() {    
    var iter = generator.apply(this, arguments);

    return Promise.resolve().then(function onValue(lastValue){
      var result = iter.next(lastValue); 

      var done  = result.done;
      var value = result.value;

      if (done) return value; // generator done, resolve promise
      return Promise.resolve(value).then(onValue, iter.throw.bind(iter)); // repeat
    });
  };
}

Comme vous pouvez le voir value (résultat d'une fonction asynchrone task{N}) doit être une promesse. Vous ne pouvez pas faire cela avec des rappels.

Reste à implémenter la technique spawn dans le langage lui-même. Nous remplaçons donc spawn par async et yield par await et arrivons à ES7 async/wait :

var ourImportantFunction = async function() {
    //... some code 1
    var val1 = await task1();
    //... some code 2
    var val2 = await task2(val1);
    //... some code 3
    var val3 = await task3(val2);

    return val3;
}

Je vous recommande de regarder cette vidéo pour mieux comprendre cela et quelques autres techniques à venir. Si le gars parle trop vite pour vous, ralentissez la vitesse de jeu ("paramètres" dans le coin inférieur droit, ou appuyez simplement sur [shift + <])

Quel est le meilleur: juste des rappels, ou des promesses, ou des promesses avec des générateurs - c'est une question très subjective. Les rappels sont la solution la plus rapide possible à l'heure actuelle (les performances des promesses natives sont très mauvaises maintenant). Les promesses faites avec des générateurs vous permettent d'écrire du code asynchrone de façon synchrone. Mais pour l'instant, ils sont beaucoup plus lents que les rappels simples.

91
alexpods

Les promesses et les générateurs sont différents modèles de logiciels (constructions):

  1. http://en.wikipedia.org/wiki/Futures_and_promises
  2. http://en.wikipedia.org/wiki/Generator_ (computer_programming)

En fait, les générateurs ne sont pas asynchrones.

Les générateurs sont utiles lorsque vous devez obtenir une série de valeurs non pas à la fois, mais une par demande. Le générateur retournera la valeur suivante immédiatement (de manière synchrone) à chaque appel jusqu'à ce qu'il atteigne la fin de la séquence (ou sans fin en cas de série infinie).

Les promesses sont utiles lorsque vous devez "différer" la valeur, qui peut ne pas être calculée (ou ne pas être disponible) pour le moment. Lorsque la valeur est disponible - c'est la valeur entière (qui n'en fait pas partie) même s'il s'agit d'un tableau ou d'une autre valeur complexe.

Vous pouvez voir plus de détails et d'exemples dans les articles wikipedia.

10
Ivan Samygin