web-dev-qa-db-fra.com

Différence entre le rendement asynchrone/wait et ES6 avec des générateurs

Je viens de lire cet article fantastique -

https://www.promisejs.org/generators/

et il met clairement en évidence cette fonction, qui est une fonction d'aide à la gestion des fonctions du générateur:

function async(makeGenerator){
  return function () {
    var generator = makeGenerator.apply(this, arguments);

    function handle(result){
      // result => { done: [Boolean], value: [Object] }
      if (result.done) return Promise.resolve(result.value);

      return Promise.resolve(result.value).then(function (res){
        return handle(generator.next(res));
      }, function (err){
        return handle(generator.throw(err));
      });
    }

    try {
      return handle(generator.next());
    } catch (ex) {
      return Promise.reject(ex);
    }
  }
}

que je suppose est plus ou moins la façon dont le mot clé async est implémenté avec async/await. La question est donc, si tel est le cas, quelle est la différence entre le mot clé await et le mot clé yield? await transforme-t-il toujours quelque chose en promesse, alors que yield ne donne aucune telle garantie? C'est ma meilleure supposition!

Vous pouvez également voir à quel point async/wait ressemble à un rendement avec des générateurs dans cet article où il décrit la fonction 'spawn': https://jakearchibald.com/2014/es7-async-functions/

59
Alexander Mills

Eh bien, il s’avère qu’il existe une relation très étroite entre async/wait et générateurs. Et je crois qu'async/wait sera toujours construit sur des générateurs. Si vous regardez la façon dont Babel transforme Async/wait:

Babel prend ceci:

this.it('is a test', async function () {

    const foo = await 3;
    const bar = await new Promise(function (resolve) {
        resolve('7');
    });
    const baz = bar * foo;
    console.log(baz);

});

et le transforme en cette

function _asyncToGenerator(fn) {
    return function () {
        var gen = fn.apply(this, arguments);
        return new Promise(function (resolve, reject) {
            function step(key, arg) {
                try {
                    var info = gen[key](arg);
                    var value = info.value;
                } catch (error) {
                    reject(error);
                    return;
                }
                if (info.done) {
                    resolve(value);
                } else {
                    return Promise.resolve(value).then(function (value) {
                        return step("next", value);
                    }, function (err) {
                        return step("throw", err);
                    });
                }
            }

            return step("next");
        });
    };
}


this.it('is a test', _asyncToGenerator(function* () {   // << now it's a generator

    const foo = yield 3;    // << now it's yield not await
    const bar = yield new Promise(function (resolve) {
        resolve('7');
    });
    const baz = bar * foo;
    console.log(baz);
}));

vous faites le calcul.

Cela donne l’impression que le mot-clé asynchrone n’est que cette fonction d’emballage, mais si c’est le cas, le temps d’attendre devient alors un rendement, il y aura probablement un peu plus de détails plus tard, quand ils seront natifs.

31
Alexander Mills

yield peut être considéré comme la pierre angulaire de await. yield prend la valeur qui lui est attribuée et la transmet à l'appelant. L'appelant peut alors faire ce qu'il veut avec cette valeur (1). Plus tard, l'appelant peut redonner au générateur une valeur (via generator.next()) qui devient le résultat de l'expression yield (2), ou une erreur qui semble provenir de l'expression yield (3).

async-await peut être considéré comme utilisant yield. En (1), l'appelant (c'est-à-dire le pilote async-await - similaire à la fonction que vous avez publiée) encapsulera la valeur dans une promesse en utilisant un algorithme similaire à new Promise(r => r(value) (remarque, pas Promise.resolve, mais ce n'est pas grave ). Il attend ensuite que la promesse soit résolue. S'il remplit, il renvoie la valeur remplie à (2). S'il refuse, le motif de rejet est considéré comme une erreur en (3).

Donc, l'utilité de async-await est cette machine qui utilise yield pour déballer la valeur générée sous forme de promesse et renvoyer sa valeur résolue, en répétant jusqu'à ce que la fonction retourne sa valeur finale.

36
Arnavion

quelle est la différence entre le mot clé await et le mot clé yield?

Le mot clé await ne doit être utilisé que dans async functions, tandis que le mot clé yield ne doit être utilisé que dans le générateur function*s. Et ceux-ci sont évidemment différents aussi: l'un retourne des promesses, l'autre renvoie des générateurs.

await transforme-t-il toujours quelque chose en promesse, alors que yield ne donne aucune telle garantie?

Oui, await appellera Promise.resolve sur la valeur attendue.

yield ne donne que la valeur en dehors du générateur.

25
Bergi

tldr;

Utiliser Async/Attendre 99% du temps sur les générateurs. Pourquoi?

  1. Async/Await remplace directement le flux de travail le plus courant des chaînes de promesses permettant au code d'être déclaré comme s'il était synchrone, ce qui le simplifie considérablement.

  2. Les générateurs font abstraction du cas d'utilisation dans lequel vous appelez une série d'opérations asynchrones qui dépendent les unes des autres et finissent par être dans un état "terminé". L'exemple le plus simple consisterait à parcourir les résultats qui renverraient éventuellement le dernier ensemble, mais vous n'appelleriez une page qu'au besoin, pas immédiatement l'un après l'autre.

  3. Async/Await est en réalité une abstraction construite sur des générateurs pour faciliter le travail avec les promesses.

Voir l'explication très détaillée d'Async/Await vs. Generators

4
Jason Sebring

Essayez ces programmes de test que je comprenais attendre/async avec des promesses

Programme n ° 1: sans promesses, il ne s'exécute pas en séquence  

function functionA() {
    console.log('functionA called');
    setTimeout(function() {
        console.log('functionA timeout called');
        return 10;
    }, 15000);

}

function functionB(valueA) {
    console.log('functionB called');
    setTimeout(function() {
        console.log('functionB timeout called = ' + valueA);
        return 20 + valueA;
    }, 10000);
}

function functionC(valueA, valueB) {

    console.log('functionC called');
    setTimeout(function() {
        console.log('functionC timeout called = ' + valueA);
        return valueA + valueB;
    }, 10000);

}

async function executeAsyncTask() {
    const valueA = await functionA();
    const valueB = await functionB(valueA);
    return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
    console.log('response called = ' + response);
});
console.log('program ended');

programme 2: avec promesses:

function functionA() {
    return new Promise((resolve, reject) => {
        console.log('functionA called');
        setTimeout(function() {
            console.log('functionA timeout called');
            // return 10;
            return resolve(10);
        }, 15000);
    });   
}

function functionB(valueA) {
    return new Promise((resolve, reject) => {
        console.log('functionB called');
        setTimeout(function() {
            console.log('functionB timeout called = ' + valueA);
            return resolve(20 + valueA);
        }, 10000);

    });
}

function functionC(valueA, valueB) {
    return new Promise((resolve, reject) => {
        console.log('functionC called');
        setTimeout(function() {
            console.log('functionC timeout called = ' + valueA);
            return resolve(valueA + valueB);
        }, 10000);

    });
}

async function executeAsyncTask() {
    const valueA = await functionA();
    const valueB = await functionB(valueA);
    return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
    console.log('response called = ' + response);
});
console.log('program ended');
3
Kamal Kumar

À bien des égards, les générateurs sont un sur-ensemble d'async/wait. Actuellement, async/wait a des traces de pile plus propres que co , la librairie la plus populaire basée sur un générateur async/wait-like. Vous pouvez implémenter votre propre style async/wait en utilisant des générateurs et ajouter de nouvelles fonctionnalités, telles que la prise en charge intégrée de yield sur des promesses non tenues ou la construction sur des observables RxJS.

En bref, les générateurs vous offrent plus de flexibilité et les bibliothèques basées sur des générateurs ont généralement plus de fonctionnalités. Mais async/wait est une partie essentielle du langage, il est standardisé et ne changera pas, et vous n’avez pas besoin d’une bibliothèque pour l’utiliser. J'ai un article blog avec plus de détails sur la différence entre async/wait et les générateurs.

0
vkarpov15