Supposons que j'ai le code suivant.
function divide(numerator, denominator) {
return new Promise((resolve, reject) => {
if(denominator === 0){
reject("Cannot divide by 0");
return; //superfluous?
}
resolve(numerator / denominator);
});
}
Si mon objectif est d'utiliser reject
pour sortir tôt, dois-je prendre l'habitude d' return
en prendre aussi immédiatement après?
Réponses:
Le
return
but est de terminer l'exécution de la fonction après le rejet et d'empêcher l'exécution du code après celle-ci.Dans ce cas, il empêche l'
resolve(numerator / denominator);
exécution de ce qui n'est pas strictement nécessaire. Cependant, il est toujours préférable de mettre fin à l'exécution pour éviter un éventuel piège à l'avenir. De plus, c'est une bonne pratique pour éviter d'exécuter du code inutilement.Contexte
Une promesse peut être dans l'un des 3 états suivants:
Lorsqu'une promesse est remplie ou rejetée, elle restera indéfiniment dans cet état (réglée). Ainsi, le rejet d'une promesse tenue ou la réalisation d'une promesse rejetée n'auront aucun effet.
Cet exemple d'extrait montre que bien que la promesse ait été tenue après avoir été rejetée, elle est restée rejetée.
Alors, pourquoi devons-nous revenir?
Bien que nous ne puissions pas changer un état de promesse réglé, le rejet ou la résolution n'arrêtera pas l'exécution du reste de la fonction. La fonction peut contenir du code qui créera des résultats déroutants. Par exemple:
Même si la fonction ne contient pas un tel code pour le moment, cela crée un éventuel piège futur. Un futur refactor peut ignorer le fait que le code est toujours exécuté après le rejet de la promesse et sera difficile à déboguer.
Arrêt de l'exécution après résolution / rejet:
Il s'agit de flux de contrôle JS standard.
resolve
/reject
:Afficher l'extrait de code
resolve
/reject
- puisque la valeur de retour du rappel est ignorée, nous pouvons enregistrer une ligne en renvoyant l'instruction de rejet / résolution:Afficher l'extrait de code
Afficher l'extrait de code
Je préfère utiliser l'une des
return
options car le code est plus plat.la source
return
est présent ou non, car une fois qu'un état de promesse a été défini, il ne peut pas être modifié, donc appelerresolve()
après l'appelreject()
ne fera rien, sauf utiliser quelques cycles CPU supplémentaires. Moi-même, j'utiliserais lereturn
juste du point de vue de la propreté et de l'efficacité du code, mais ce n'est pas obligatoire dans cet exemple spécifique.Promise.try(() => { })
place de la nouvelle promesse et évitez d'utiliser les appels de résolution / rejet. Au lieu de cela, vous pouvez simplement écrirereturn denominator === 0 ? throw 'Cannot divide by zero' : numerator / denominator;
que j'utilisePromise.try
comme moyen de lancer une promesse ainsi que d'éliminer les promesses enveloppées dans des blocs try / catch qui sont problématiques.reject
qui fait quelque chose de cher, comme se connecter à des bases de données ou des points de terminaison API? Tout cela serait inutile et vous coûterait de l'argent et des ressources, en particulier par exemple si vous vous connectez à quelque chose comme une base de données AWS ou un point de terminaison API Gateway. Dans ce cas, vous utiliserez certainement un retour afin d'éviter l'exécution de code inutile.return
.Un idiome commun, qui peut ou non être votre tasse de thé, consiste à combiner le
return
avec lereject
, pour rejeter simultanément la promesse et quitter la fonction, de sorte que le reste de la fonction, y compris le,resolve
ne soit pas exécuté. Si vous aimez ce style, il peut rendre votre code un peu plus compact.Cela fonctionne très bien car le constructeur Promise ne fait rien avec aucune valeur de retour, et en tout cas
resolve
etreject
ne retourne rien.Le même idiome peut être utilisé avec le style de rappel indiqué dans une autre réponse:
Encore une fois, cela fonctionne bien car la personne qui appelle
divide
ne s'attend pas à ce qu'elle retourne quoi que ce soit et ne fait rien avec la valeur de retour.la source
reject
statutTechniquement, cela n'est pas nécessaire ici 1 - car une promesse peut être résolue ou rejetée, exclusivement et une seule fois. Le premier résultat Promise l'emporte et chaque résultat suivant est ignoré. Ceci est différent des rappels de style Node.
Cela étant dit, il est de bonne pratique propre de s'assurer que exactement un est appelé, lorsque cela est possible, et en effet dans ce cas, car il n'y a plus de traitement asynchrone / différé. La décision de «revenir tôt» n'est pas différente de la fin d'une fonction lorsque son travail est terminé - par rapport à la poursuite d'un traitement non lié ou inutile.
Le retour au moment opportun (ou l'utilisation de conditions pour éviter d'exécuter «l'autre» cas) réduit les risques d'exécuter accidentellement du code dans un état non valide ou d'effectuer des effets secondaires indésirables; et en tant que tel, il rend le code moins susceptible de «casser de façon inattendue».
1 Cette réponse technique dépend également du fait que dans ce cas le code après le "retour", s'il est omis, n'entraînera pas d'effet secondaire. JavaScript divisera heureusement par zéro et retournera soit + Infinity / -Infinity ou NaN.
la source
Si vous ne "retournez" pas après une résolution / rejet, de mauvaises choses (comme une redirection de page) peuvent se produire après que vous vouliez que cela s'arrête. Source: je suis tombé sur cela.
la source
La réponse d'Ori explique déjà qu'il n'est pas nécessaire de
return
mais c'est une bonne pratique. Notez que le constructeur de promesses est sûr de lancer, donc il ignorera les exceptions levées passées plus tard dans le chemin, vous avez essentiellement des effets secondaires que vous ne pouvez pas facilement observer.Notez
return
qu'ing early est également très courant dans les rappels:Ainsi, bien que ce soit une bonne pratique dans les promesses, elle est requise avec les rappels. Quelques notes sur votre code:
la source
Dans de nombreux cas, il est possible de valider les paramètres séparément et de renvoyer immédiatement une promesse rejetée avec Promise.reject (raison) .
la source