web-dev-qa-db-fra.com

Différence entre `return wait promise` et` return promise`

Compte tenu des exemples de code ci-dessous, existe-t-il une différence de comportement et, dans l'affirmative, quelles sont ces différences?

return await promise

async function delay1Second() {
  return (await delay(1000));
}

return promise

async function delay1Second() {
  return delay(1000);
}

Si je comprends bien, le premier système aurait une gestion des erreurs au sein de la fonction asynchrone et des erreurs jailliraient de la promesse de la fonction asynchrone. Cependant, le second nécessiterait un tick de moins. Est-ce correct?

Cet extrait est juste une fonction commune pour renvoyer une promesse de référence.

function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}
59
PitaJ

La plupart du temps, il n'y a pas de différence observable entre return et return await. Les deux versions de delay1Second ont exactement le même comportement observable (mais en fonction de l’implémentation, le return await version pourrait utiliser légèrement plus de mémoire car un objet intermédiaire Promise pourrait être créé).

Cependant, comme @PitaJ l’a souligné, il existe un cas où il existe une différence: si le return ou le return await est imbriqué dans un bloc try-catch. Considérez cet exemple

async function rejectionWithReturnAwait () {
  try {
    return await Promise.reject(new Error())
  } catch (e) {
    return 'Saved!'
  }
}

async function rejectionWithReturn () {
  try {
    return Promise.reject(new Error())
  } catch (e) {
    return 'Saved!'
  }
}

Dans la première version, la fonction asynchrone attend la promesse refusée avant de renvoyer le résultat, ce qui a pour effet de transformer le refus en exception et d'atteindre la clause catch; la fonction retournera donc une promesse en résolvant à la chaîne "Saved!".

La deuxième version de la fonction retourne cependant la promesse rejetée directement sans l'attendre dans la fonction asynchrone, ce qui signifie que la casse catch est not appelé et l'appelant obtient le rejet à la place.

95
Denis Washington

Comme le mentionnent d’autres réponses, il est probable que le fait de laisser la promesse rebondir directement en la laissant un léger avantage sur le plan des performances, tout simplement parce que vous n’avez pas à attendre le résultat avant de le ré-emballer à nouveau. Cependant, personne n'a encore parlé de optimisation des appels en queue .

optimisation de l'appel final , ou "appels appropriés" , est une technique utilisée par l'interpréteur pour optimiser la pile d'appels. Actuellement, pas beaucoup de runtimes le supportent encore - même si cela fait techniquement partie du ES6 Standard -, mais un support pourrait éventuellement être ajouté ultérieurement, afin que vous puissiez vous y préparer. en écrivant un bon code dans le présent.

En un mot, TCO (ou PTC) optimise la pile d'appels en pas en ouvrant une nouvelle image pour une fonction renvoyée directement par une autre fonction. Au lieu de cela, il réutilise le même cadre.

async function delay1Second() {
  return delay(1000);
}

Puisque delay() est directement renvoyé par delay1Second(), les environnements d'exécution prenant en charge PTC ouvriront d'abord un cadre pour delay1Second() (la fonction extérieure), mais au lieu d'ouvrir another frame pour delay() (la fonction interne), il ne fera que réutiliser le même cadre que celui qui a été ouvert pour la fonction externe. Ceci optimise la pile car il peut empêcher un débordement de pile (hehe) avec de très grandes fonctions récursives, par exemple, fibonacci(5e+25). En gros, cela devient une boucle, ce qui est beaucoup plus rapide.

PTC est activé uniquement lorsque la fonction interne est directement renvoyée. Il n’est pas utilisé lorsque le résultat de la fonction est modifié avant son renvoi, par exemple, si vous aviez return (delay(1000) || null) ou return await delay(1000).

Mais comme je l’ai dit, la plupart des runtimes et des navigateurs ne prennent pas encore PTC en charge. Par conséquent, cela ne fait probablement pas une énorme différence maintenant, mais cela ne pourrait pas nuire à la viabilité de votre code.

Lisez la suite de cette question: Node.js: Y a-t-il des optimisations pour les appels de queue dans les fonctions asynchrones?

4
chharvey

Il est difficile de répondre à cette question, car cela dépend en pratique de la façon dont votre transpiler (probablement babel) rend réellement async/await. Les choses qui sont claires malgré tout:

  • Les deux implémentations devraient se comporter de la même manière, bien que la première implémentation may ait un moins Promise dans la chaîne.

  • Surtout si vous supprimez le await inutile, la deuxième version ne nécessiterait aucun code supplémentaire du transpiler, contrairement à la première.

Ainsi, du point de vue des performances du code et du débogage, la deuxième version est préférable, bien que très légèrement, alors que la première version présente un léger avantage en termes de lisibilité, en ce sens qu’elle indique clairement qu’elle retourne une promesse.

2
nrabinowitz