web-dev-qa-db-fra.com

Les classes JavaScript ES6 sont-elles utiles avec les bases de code asynchrones?

Que peut ES6 Classes fournir, en tant que modèle d'organisation, au code asynchrone. Ci-dessous un exemple avec ES7 async/wait, une classe ES6 peut-elle avoir une méthode asynchrone ou un constructeur dans ES7?

Est-ce que je peux faire:

class Foo {
    async constructor() {
        let res = await getHTML();
        this.res = res
    }
}

Et, sinon, comment un constructeur devrait-il fonctionner?

class Foo {
    constructor() {
        getHTML().then( function (res) {
            this.res = res
        }
    }
}

Si aucun de ces modèles ne fonctionne, un constructeur (et de surcroît, des classes) dans un ES6 class peut-il prendre en charge toute forme d'asynchronicité opérant sur l'état de l'objet? Ou sont-ils uniquement pour des bases de code purement synchrones? Les exemples ci-dessus sont dans le constructeur, mais ils n'ont pas besoin de l'être .. Abaisser le problème d'un niveau supplémentaire ..

class Foo {
    myMethod () {
      /* Can I do anything async here */
    }
}

Ou, avec un getter ...

class Foo {
    get myProp() {
        /* Is there any case that this is usefully asynchronous */
    }
}

Le seul exemple auquel je puisse penser est d'exécuter quelque chose en parallèle dans la même méthode/le même constructeur/le même getter, mais de résoudre le problème avant de conclure. Je suis juste dérouté car il semble qu'avec tous les Push à des bibliothèques totalement asynchrones, cela ne sert qu'à confondre les choses. À l'exception des exemples de manuels, je ne trouve pas d'application à laquelle ils servent.

47
Evan Carroll

Puis-je faire async constructor()

Non, c'est une erreur de syntaxe - tout comme constructor* (). Un constructeur est une méthode qui ne retourne rien (aucune promesse, aucun générateur), il initialise uniquement l'instance.

Et, sinon, comment un constructeur devrait-il travailler

Un tel constructeur ne devrait pas exister du tout, voir Est-ce une mauvaise pratique de renvoyer une promesse par une fonction constructeur?

Les classes ES6 peuvent-elles prendre en charge toute forme d’asynchronisme opérant sur l’état de l’objet? Ou sont-ils uniquement pour des bases de code purement synchrones?

Oui, vous pouvez utiliser des méthodes asynchrones (même avec la syntaxe async proposée) sur les classes, et les getters peuvent également renvoyer des promesses.

Cependant, vous devrez décider de ce qui doit se passer lorsqu'une méthode est appelée alors qu'un processus asynchrone est toujours actif. Si vous souhaitez que toutes vos opérations soient séquencées, vous devez stocker l'état de votre instance dans une promesse que vous pouvez chaîner pour la fin de cette séquence. Ou, si vous souhaitez autoriser des opérations parallèles, la meilleure approche consiste à rendre vos instances immuables et à renvoyer une promesse pour une autre instance.

42
Bergi

ECMAScript 2017 est destiné à être des classes de méthodes asynchrones.

Invoquer une autre fonction asynchrone ou de retour de promesse est un one-line!

Le code très expressif se lit sans interruption de haut en bas, quelle que soit l'exécution différée

Si vous avez des rappels, des gestionnaires d'erreur alternatifs, une exécution parallèle ou d'autres besoins non satisfaits, instanciez les promesses dans le corps de la fonction. Il est préférable d’avoir du code dans le corps de la fonction plutôt que dans un exécuteur de promesse et de noter qu’il n’existe pas de code de rappel try-catch wrapping: ne rien faire de proche ici.

La méthode async peut renvoyer une promesse, une valeur régulière ou un jet

Les callbacks que les gens de Node.js aimaient beaucoup, nous allons maintenant les haïr avec passion: ils doivent tous être emballés dans des promesses

La beauté de async/wait, c’est que les erreurs jaillissent implicitement

class MyClass {
  async doEverything() {
    const sumOfItAll = await http.scrapeTheInternet() +
      await new Promise((resolve, reject) =>
        http.asyncCallback((e, result) => !e ? resolve(result) : reject(e)))
    return this.resp = sumOfItAll
  }
}

Si limité à ECMAScript 2015 et non asynchrone, renvoyez les valeurs de promesse:

class ES2015 {
  fetch(url) {
    return new Promise((resolve, reject) =>
      http.get(url, resolve).on('error', reject))
      .then(resp => this.resp = resp) // plain ECMAScript stores result
      .catch(e => { // optional internal error handler
        console.error(e.message)
        throw e // if errors should propagate
      })
  }
}

Vous posez vraiment la question à propos de cette version ECMAScript 2015: tout comportement souhaité peut être codé à l'aide de la construction de promesse retournée.

Si vous voulez vraiment, vraiment exécuter des promesses dans le constructeur, il est judicieux de passer des fonctions then-catch ou de fournir une structure de rappel afin que les consommateurs puissent agir en cas de réalisation ou de rejet d'une promesse. Dans le constructeur, il est également judicieux d’attendre que nextTick/.then se produise avant d’effectuer un travail réel.

Chaque promesse nécessite une attrape finale ou il y aura des problèmes

9
Harald Rudell

Les classes peuvent également être utiles pour organiser des tâches asynchrones avec l'utilisation exclusive de méthodes statiques .

class Organizer {
    static async foo() {
        const data = await this.bar();
        data.key = value;
        return data;
    }
    static async bar() {
        return {foo:1, bar:2}
    }
};

Organizer.foo();

Bien entendu, cela ne diffère pas de la création d'un littéral d'objet simple ou d'un nouveau fichier et de son inclusion, sauf que vous pouvez plus proprement extend le.

9
Evan Carroll

Ceci est une réponse tardive, mais la raison pour laquelle votre deuxième exemple ne fonctionne pas est due à une erreur de contexte. Lorsque vous passez un function () {} en tant qu'argument à Promise.prototype.then(), le lexique this de la fonction sera la fonction elle-même et non la classe. C'est pourquoi la définition de this.res semble ne rien faire: this, dans ce cas, fait référence à la portée de la fonction.

Il existe plusieurs façons d’accéder à une portée externe en Javascript, la plus classique (que vous voyez abondamment dans le code ES5) étant:

class Foo {
  constructor() {
    var _this = this

    getHTML().then(function (res) {
      _this.res = res
    })
  }
}

En faisant référence à la classe this, vous pouvez y accéder dans les étendues internes.

La méthode ES6 consiste à utiliser les fonctions de flèche , qui ne créent pas de nouvelle portée, mais "gardent" celle actuelle.

class Foo {
  constructor() {
    getHTML().then(res => this.res = res)
  }
}

Mis à part les problèmes de contexte, ce n'est pas encore un modèle asynchrone optimal à mon avis, car vous n'avez aucun moyen de savoir quand getHTML() est terminé ou pire, a échoué. Ce problème est résolu avec élégance avec fonctions asynchrones . Bien que vous ne puissiez pas créer async constructor () { ... }, vous pouvez créer une promesse dans le constructeur et await dans les fonctions qui en dépendent.

Exemple Gist d'une propriété asynchrone dans un constructeur de classe.

0
Christophe Marois