J'ai utilisé async attendre avec Babel dans mon projet ReactJS. J'ai découvert un usage pratique avec React setState que je voudrais simplement mieux comprendre. Considérons ce code:
handleChange = (e) => {
this.setState({[e.target.name]: e.target.value})
console.log('synchronous code')
}
changeAndValidate = async (e) => {
await this.handleChange(e)
console.log('asynchronous validation code')
}
componentDidUpdate() {
console.log('updated component')
}
Mon intention était que le code de validation asynchrone soit exécuté après la mise à jour du composant. Et il fonctionne! Le journal de la console résultant montre:
synchronous code
updated component
asynchronous validation code
Le code de validation ne sera exécuté qu'une fois que handleChange aura mis à jour l'état et que le nouvel état sera rendu.
Habituellement, pour exécuter le code après la mise à jour de l'état, vous devez utiliser un rappel après this.setState. Ce qui signifie que si vous voulez exécuter quoi que ce soit après handleChange, vous devez lui attribuer un paramètre de rappel qui est ensuite transmis à setState. Pas beau. Mais dans l'exemple de code, wait sait en quelque sorte que handleChange est terminé après la mise à jour de l'état ... Mais je pensais que wait ne fonctionnait qu'avec des promesses et attendait une promesse à résoudre avant de continuer. Il n'y a pas de promesse ni de résolution dans le changement ... Comment sait-on quoi attendre?
L'implication semble être que setState est exécuté de manière asynchrone et attend en quelque sorte au courant de son exécution. Peut-être que setState utilise les promesses en interne?
Versions:
réagir: "^ 15.4.2"
babel-core: "^ 6.26.0"
babel-preset-env: "^ 1.6.0",
babel-preset-react: "^ 6.24.1",
babel-preset-stage-0: "^ 6.24.1"
babel-plugin-system-import-transformer: "^ 3.1.0",
babel-plugin-transform-decorators-legacy: "^ 1.3.4",
babel-plugin-transform-runtime: "^ 6.23.0"
this.handleChange , à droite de wait , exécute:
2.1. setState exécute son programme de mise à jour, mais parce que setState ne garantit pas une mise à jour immédiate, il est possible que la mise à jour soit programmée ultérieurement(peu importe si c'est immédiat ou ultérieur. avec le temps, tout ce qui compte, c’est que c’est prévu)
2.2. console.log ('code synchrone') s'exécute ...
2.3. this.handleChange quitte ensuite en retournant undefined (retourne undefined car les fonctions retournent undefined sauf spécification contraire explicite)
wait prend alors ce undefined et comme ce n’est pas une promesse, il la convertit en promesse résolue en utilisant Promise.resolve (undefined) et l’attend - il n’est pas immédiatement disponible car derrière le scènes il est passé à sa méthode .then qui est asynchrone:
«Les rappels passés en promesse ne seront jamais appelés avant le achèvement de l'exécution en cours de la boucle d'événement JavaScript ”
3.1. cela signifie que undefined sera placé à l’arrière de la file d’événements, (c’est-à-dire qu’il se trouve maintenant derrière notre programme de mise à jour setState dans la file d’événements…)
boucle d'événement atteint finalement et récupère notre setState update, qui s'exécute maintenant ...
boucle d'événement atteint et récupère undefined , qui correspond à undefined (nous pourrions le stocker si nous le souhaitions, d'où le = généralement utilisé devant attendre pour stocker le résultat résolu)
5.1. Promise.resolve () est maintenant terminé, ce qui signifie wait n'est plus en vigueur, le reste de la fonction peut donc reprendre
Je n'ai pas encore testé cela, mais voici ce que je pense qui se passe:
La undefined
renvoyée par await
est mise en file d'attente après le rappel setState
. La await
exécute un Promise.resolve
sous (dans le regenerator-runtime
) qui, à son tour, permet de contrôler l'élément suivant de la boucle d'événements.
C'est donc un hasard si le callback setState
est mis en file d'attente avant le await
.
Vous pouvez le tester en mettant un setTimeout (f => f, 0) autour de setState
.
regenerator-runtime
dans babel
est essentiellement une boucle qui utilise Promise.resolve
pour donner le contrôle. Vous pouvez voir l’intérieur du _asyncToGenerator , il a un Promise.resolve
.
La rv
ou la valeur de retour de wait est définie comme suit:
rv
Returns the fulfilled value of the promise, or the value itself if it's not a Promise.
Ainsi, puisque handleChange n'est pas une valeur asynchrone ou promise, il renvoie simplement la valeur naturelle (dans ce cas, il n'y a pas de retour, donc undefined
). Ainsi, il n’ya pas de déclencheur de boucle d’événement asynchrone ici pour "lui faire savoir que le changement est terminé", il est simplement exécuté dans l’ordre que vous lui avez donné.
setState()
ne met pas toujours immédiatement à jour le composant doc
Mais c'est peut-être le cas ici.
Si vous souhaitez remplacer le rappel par une promesse, vous pouvez l'implémenter vous-même:
setStateAsync(state) {
return new Promise((resolve) => {
this.setState(state, resolve)
});
}
handleChange = (e) => {
return this.setStateAsync({[e.target.name]: e.target.value})
}
ref: https://medium.com/front-end-hacking/async-await-with-react-lifecycle-methods-802e7760d802