ES6 - générateurs et fonctions asynchrones

insérez la description de l'image ici

Introduction

Avant la naissance d’ES6, il existait environ quatre méthodes de programmation asynchrone.

  • Rappeler
  • écouteur d'événements
  • publier/s'abonner
  • Objet de promesse

回调函数Il n'y a pas de problème en soi, son problème est que l'imbrication de plusieurs fonctions de rappel provoquera un enfer de rappel, ce qui est très défavorable à la maintenance du code et à la confusion logique ;

PromiseDes objets sont proposés pour résoudre ce problème. Il ne s'agit pas d'une nouvelle fonctionnalité grammaticale, mais d'une nouvelle façon d'écrire, qui permet de transformer l'imbrication de fonctions de rappel en appels en chaîne. En utilisant Promise pour lire plusieurs fichiers en continu, la méthode d'écriture est la suivante :

var readFile = require('fs-readfile-promise');
readFile(fileA)
.then(function (data) {
    
    
  console.log(data.toString());
})
.then(function () {
    
    
  return readFile(fileB);
})
.then(function (data) {
    
    
  console.log(data.toString());
})
.catch(function (err) {
    
    
  console.log(err);
});

On voit que la méthode d'écriture de Promise n'est qu'une amélioration de la fonction de rappel. Après avoir utilisé la méthode then, l'exécution en deux étapes de la tâche asynchrone est plus claire. A part cela, il n'y a rien de nouveau.

Le plus gros problème avec Promise est la redondance du code. La tâche d'origine est enveloppée par Promise. Quelle que soit l'opération, elle apparaît en un coup d'œil, et la sémantique d'origine devient très floue ;

那么,有没有更好的写法呢?:

Les fonctions du générateur amènent la programmation asynchrone JavaScript à un tout autre niveau.


2. Présentation

La fonction Générateur est l'une des solutions apportées par ES6 pour résoudre la programmation asynchrone ;​

L'ensemble de la fonction Generator est une tâche asynchrone encapsulée ou un conteneur pour les tâches asynchrones. L'endroit où l'opération asynchrone doit être suspendue est marqué par l'instruction rendement. La méthode d'exécution de la fonction Générateur est la suivante.

function* gen(x) {
    
    
  var y = yield x + 2;
  return y;
}

var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }

Dans le code ci-dessus, l'appel de la fonction Generator renverra un pointeur interne (c'est-à-dire un traverseur) g. C'est un autre endroit où la fonction Générateur est différente des fonctions ordinaires, c'est-à-dire que son exécution ne renverra pas un résultat, mais un objet pointeur. L'appel de la méthode suivante du pointeur g déplacera le pointeur interne (c'est-à-dire le premier paragraphe de l'exécution de la tâche asynchrone), pointant vers la première instruction rendement rencontrée, et l'exemple ci-dessus est exécuté jusqu'à x + 2.

En d’autres termes, le rôle de la méthode suivante est d’exécuter la fonction Générateur par étapes. Chaque fois que la méthode suivante est appelée, un objet sera renvoyé, représentant les informations de l'étape en cours (attribut value et attribut done). L'attribut value est la valeur de l'expression derrière l'instruction rendement, indiquant la valeur de l'étape en cours ; l'attribut done est une valeur booléenne, indiquant si la fonction Générateur a été exécutée, c'est-à-dire s'il y a une étape suivante.

3. Caractéristiques

​ 1. Pour définir Generatorune fonction, vous devez ajouter un numéro après la fonction *:

function* helloWorldGenerator(){
    
    ...}

2. L'appel de la fonction n'imprimera aucune donnée de fonction, mais renverra un objet pointeur ;

3. Chaque fois que la méthode suivante de l'objet pointeur est appelée pour exécuter le contenu de la fonction génératrice, le résultat renvoyé est similaire à celui de l'itérateur ;


Quatrièmement, implémentation de la fonction générateur

Étant donné que l'objet traverseur renvoyé par la fonction Générateur nextne peut traverser l'état interne suivant que lorsque la méthode est appelée, il fournit en fait une fonction qui peut suspendre l'exécution. yieldL'expression est le drapeau de pause.

La logique de fonctionnement de la méthode de l'objet traverseur nextest la suivante :

(1) Lorsqu'une yieldexpression est rencontrée, l'exécution de l'opération suivante est suspendue et la valeur de l'expression qui suit immédiatement est utilisée comme valeur d'attribut yieldde l'objet renvoyé .value

(2) La prochaine fois que nextla méthode est appelée, continuez à l'exécuter jusqu'à ce que l' yieldexpression suivante soit rencontrée.

(3) Si aucune nouvelle yieldexpression n'est rencontrée, elle s'exécutera jusqu'à la fin de la fonction jusqu'à returnl'instruction et utilisera returnla valeur de l'expression après l'instruction comme valuevaleur d'attribut de l'objet renvoyé.

(4) Si la fonction n'a pas d'instruction, la valeur d'attribut returnde l'objet renvoyé estvalueundefined

Voir l'exemple suivant :

      function* helloWorldGenerator() {
    
    
        yield "hello";
        console.log("111");
        yield "world";
        console.log("222");
        return "ending";
      }
      var hw = helloWorldGenerator();
      console.log(hw.next());
      console.log(hw.next());
      console.log(hw.next());

résultat de l'opération :

{
    
    value: 'hello', done: false}
111
{
    
    value: 'world', done: false}
222
{
    
    value: 'ending', done: true}

C'est-à-dire:

Chaque fois que nous appelons, nous devons utiliser next pour appeler. Chaque fois que nous utilisons un next, nous appellerons un rendement. Lorsque le programme touche le rendement, le programme tombera dans un état bloqué et continuera à s'exécuter jusqu'à ce qu'il atteigne le prochain suivant, un rendement. C'est-à-dire que chaque fois que le rendement est appelé, un rendement sera exécuté ;

La fonction Générateur n'a pas besoin de l'expression de rendement et devient alors une fonction d'exécution purement différée. comme suit:

   function* fun() {
    
    
    console.log("执行了!");
   }
   var generator = fun();
   generator.next();
   setTimeout(function () {
    
    
    generator.next();
   }, 2000);

Dans le code ci-dessus, si la fonction fun est une fonction ordinaire, elle sera exécutée lorsqu'une valeur sera attribuée au générateur de variables. Cependant, la fonction fun est une fonction Générateur, donc la fonction fun ne sera exécutée que lorsque la méthode suivante sera appelée.

De plus, il convient de noter que l'expression de rendement ne peut être utilisée que dans la fonction Générateur, et une erreur sera signalée si elle est utilisée à d'autres endroits.

Cinq, fonction asynchrone

asyncQu'est-ce qu'une fonction ? En un mot, c'est Generator 函数le sucre syntaxique de . Rend les opérations asynchrones plus pratiques.
C'est aussi une manière courante d'écrire dans notre développement quotidien ;

1. La fonction Générateur gère l'asynchronie :

readFile est une opération asynchrone, peu importe ce qu'elle fait

const gen = function* () {
    
    
  const f1 = yield readFile('/etc/fstab');
  const f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

2, traitement de fonction asynchrone asynchrone

const asyncReadFile = async function () {
    
    
  const f1 = await readFile('/etc/fstab');
  const f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

Une comparaison révélera que la fonction async consiste à remplacer l'astérisque (*) de la fonction Generator par async, et à remplacer rendement par wait, c'est tout. Son plus grand avantage est que le code est écrit un peu comme une opération synchrone : si vous supprimez yieldla commande, c'est exactement la même chose ;

L'amélioration de la fonction async par rapport à la fonction Générateur se traduit par les quatre points suivants :

(1) Actionneur intégré.

L'exécution de la fonction Générateur doit appeler nextla méthode, ou utiliser le module co pour l'exécuter réellement et obtenir le résultat final.
Et async函数自带执行器. Autrement dit, l’exécution d’une fonction asynchrone est exactement la même que celle d’une fonction normale, avec une seule ligne.

(2) Meilleure sémantique.

async和await, la sémantique est plus claire que l'astérisque et le rendement. async signifie qu'il y a des opérations asynchrones dans la fonction, et wait signifie que l'expression qui suit immédiatement doit attendre le résultat.

(3) Applicabilité plus large.

Le module co stipule que la commande rendement ne peut être suivie que d'une fonction Thunk ou d'un objet Promise, qui 而async函数的await命令后面peut être un objet Promise et une valeur de type primitif (nombre, chaîne et valeur booléenne, mais elle sera automatiquement convertie en un objet de romise immédiatement résolu).

(4) La valeur de retour est Promesse.

async函数的返回值是 Promise 对象, ce qui est beaucoup plus pratique que la valeur de retour de la fonction Generator étant un objet Iterator. Vous pouvez utiliser la méthode then pour spécifier l'action suivante.

De plus, async函数cela peut être considéré comme plusieurs opérations asynchrones regroupées dans un objet Promise, et awaitla commande est le sucre syntaxique de la commande interne then ;


[Documents de référence] :
1. Syntaxe de la fonction Générateur : https://es6.ruanyifeng.com/#docs/generator
2. Fonction Async : https://es6.ruanyifeng.com/#docs/async

Je suppose que tu aimes

Origine blog.csdn.net/qq_43886365/article/details/132062626
conseillé
Classement