4 votes

La liaison de données Angular ne fonctionnera pas avec async/await, mais elle fonctionnera avec promises.

Les liaisons de données ne sont pas mises à jour si leurs valeurs sont modifiées après une modification de l'ordre du jour. await déclaration.

  handle() {
    this.message = 'Works'
  }

  async handle() {
    this.message = 'Works'
  }

  async handle() {
    await new Promise((resolve, reject) => {
      resolve()
    })
    this.message = 'Works'
  }

  async handle() {
    await new Promise((resolve, reject) => {
      setTimeout(() => resolve(), 3000)
    })
    this.message = 'Doesn\'t work'
  }

  handle() {
    new Promise((resolve, reject) => {
      setTimeout(() => resolve(), 3000)
    })
    .then(() => this.message = 'Works')
  }

Pourquoi les deux derniers ne se comportent-ils pas de la même manière ? Ne sont-ils pas censés être la même chose ?

Ionic : 3.9.2

Angulaire : 5.0.3

TypeScript : 2.4.2

EDIT : J'ai rencontré un autre problème qui pourrait être utile à certains.

La modification des valeurs d'une liaison dans le constructeur se comporte différemment de ionViewDidLoad ou ngOnInit !

  constructor(private zone: NgZone) {
    // This will cause the same problems, bindings not updating
    this.handle()
  }

  constructor(private zone: NgZone) {
    // Unless you do this...
    this.zone.run(() => {
      this.handle()
    })
  }

  ionViewDidLoad() {
    // But I think this is better/cleaner
    this.handle()
  }

6voto

estus Points 5252

Angular s'appuie sur Zone.js pour la détection des changements, et Zone.js fournit cela en Parcheando chaque API qui peut fournir un comportement asynchrone.

Le problème est de savoir comment les natifs async sont mises en œuvre. Comme cela a été confirmé dans cette question ils ne se contentent pas d'envelopper le monde entier. Promise mais reposent sur des mécanismes internes qui peuvent varier d'un navigateur à l'autre.

Corrections de Zone.js Promise mais il est impossible de patcher la promesse interne qui est utilisée par async dans les implémentations actuelles du moteur (voici problème ouvert pour cela).

Habituellement (async () => {})() instanceof Promise === true . Dans le cas de Zone.js, ce n'est pas vrai ; async renvoie une instance de la fonction native Promise alors que Promise global est la promesse de zone-aware patchée par Zone.js.

Afin de faire de l'indigène async fonctionnent en Angular, la détection des changements doit être déclenchée en plus. Cela peut être fait en la déclenchant explicitement (comme une autre réponse le suggère déjà) ou en utilisant n'importe quelle API sensible à la zone. Une aide qui englobe async Le résultat de la fonction avec promesse de zone fera l'affaire :

function nativeAsync(target, method, descriptor) {
  const originalMethod = target[method];
  descriptor.value = function () {
    return Promise.resolve(originalMethod.apply(this, arguments));
  }
}

Ici est un exemple qui utilise @nativeAsync décorateur sur async des méthodes pour déclencher la détection des changements :

  @nativeAsync
  async getFoo() {
    await new Promise(resolve => setTimeout(resolve, 100));
    this.foo = 'foo';
  }

Ici est le même exemple qui n'utilise pas de mesures supplémentaires pour déclencher la détection des changements et qui, comme on pouvait s'y attendre, ne fonctionne pas comme prévu.

Il est logique de s'en tenir à l'implémentation native dans un environnement qui ne nécessite pas d'étape de transpilation. Puisque l'application Angular est censée être compilée de toute façon, le problème peut être résolu en passant de l'implémentation native à l'implémentation native. ES2017 a ES2015 o ES2016 TypeScript target .

2voto

jiali passion Points 659

Comme l'a dit Estus, actuellement zone.js ne supportent pas l'async/await natif, vous ne pouvez donc pas compiler des scripts qui ciblent les fonctions ES2017 et j'y travaille, https://github.com/angular/zone.js/pull/795 J'ai fait une démo fonctionnelle qui peut fonctionner dans nodejs, mais dans le navigateur (chrome), cela prendra encore un certain temps parce que chrome n'ouvre pas la version javascript de AsyncHooks et PromiseHooks pour le moment.

1voto

funkizer Points 2315

C'est lié à la façon dont la détection des changements fonctionne dans Angular. Voir : https://stackblitz.com/edit/angular-jajbza?file=app%2Fapp.component.ts

Je parie que Ionic utilise la stratégie OnPush par défaut, ou que vous l'avez activée, comme je l'ai fait dans le Blitz. C'est tout bon, IMO il devrait être activé par défaut de toute façon car il vous oblige à penser à ces choses et à écrire du code plus performant.

Je ne peux pas dire exactement pourquoi votre vue est mise à jour dans votre dernier exemple lorsque vous appelez .then. Peut-être que dans ce cas, le CD parvient à garder la trace de la Promise, mais pas avec les fonctions asynchrones. Les fonctions asynchrones elles-mêmes renvoient une Promise si vous ne le faites pas, donc après transpilation, async handle() renvoie quelque chose comme Promise.resolve(null), bien que le code JS réel soit probablement plus désordonné que cela.

Edit : Une autre façon, peut-être plus propre que d'appeler detectChanges manuellement, serait d'exécuter tout ce qui modifie la vue à l'intérieur de la zone d'Angular :

import { NgZone } from '@angular/core';
constructor (private zone:NgZone){}

// after await somePromise :
this.zone.run(() => { 
    this.someProperty = 'Something changed';
});

Edit2 : Intéressant, zone.run() ne change rien en fait. J'ai testé ça dans le blitz. Donc le CD manuel est le seul moyen. Une raison de plus pour éviter async/await et essayer de s'en tenir à Observables et NG's async pipe :)

1voto

ovg Points 429

Vérifiez la version de votre navigateur et ce qu'il supporte, async/await est natif avec ES2017. Si votre navigateur ne le supporte pas, utilisez l'ES2016 comme cible.

J'avais besoin de l'ES2016 pour Electron.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X