Quelle est la différence entre retourner une valeur ou Promise.resolve à partir de là ()

314

Quelle est la différence entre:

new Promise(function(res, rej) {
    res("aaa");
  })
  .then(function(result) {
    return "bbb";
  })
  .then(function(result) {
    console.log(result);
  });

et ça:

new Promise(function(res, rej) {
    res("aaa");
  })
  .then(function(result) {
    return Promise.resolve("bbb");
  })
  .then(function(result) {
    console.log(result);
  });

Je demande car j'obtiens un comportement différent Utilisation du service Angular et $ http avec le chaînage .then (). Un peu trop de code, donc d'abord l'exemple ci-dessus.

spirytus
la source
1
Quel "comportement différent" voyez-vous? Les deux exemples doivent fonctionner et se comporter approximativement de la même manière. Le Promise.resolve()deuxième exemple n'est pas nécessaire.
JLRishe
4
@pixelbits Il n'y a rien de mal à renvoyer une promesse d'un thengestionnaire, en fait, c'est un aspect clé de la promesse que vous pouvez le faire.
Notez que cela fonctionne avec thens imbriqués arbitrairement - le terme «autres langues» pour cela thenest à la fois a mapet a flatMap.
Benjamin Gruenbaum
1
à la ligne 2, pourquoi devez-vous appeler res ("aaa"), pourquoi ne pas renvoyer "aaa" être suffisant et la capture de promesse pour le résoudre () de la même manière qu'il intercepte les exceptions pour rejette ()?
Sam Liddicott
1
@SamLiddicott ayant la même question, alors que les mines sont un peu plus compliquées: new Promise((res, rej) => { return fetch('//google.com').then(() => { return "haha"; }) }).then((result) => alert(result));ce code va juste se bloquer (pas résolu pour toujours). Mais si je passe return "haha";à return res("haha");alors cela fonctionnera et alertera "haha". Le fetch (). Then () n'a-t-il pas déjà enveloppé "haha" dans une promesse résolue?
Shaung Cheng

Réponses:

138

La règle est, si la fonction qui se trouve dans le thengestionnaire renvoie une valeur, la promesse se résout / rejette avec cette valeur, et si la fonction renvoie une promesse, ce qui se passe, la thenclause suivante sera la thenclause de la promesse que la fonction a renvoyée , donc, dans ce cas, le premier exemple passe par la séquence normale de thenset imprime les valeurs comme on pourrait s'y attendre, dans le deuxième exemple, l'objet de promesse qui est renvoyé lorsque vous le faites Promise.resolve("bbb")est alors celui thenqui est invoqué lors du chaînage (à toutes fins utiles). La façon dont cela fonctionne est décrite ci-dessous plus en détail.

Citant des promesses / A + spec:

La procédure de résolution de promesse est une opération abstraite prenant en entrée une promesse et une valeur que nous désignons [[Resolve]](promise, x). Si xest un élément exploitable, il tente de faire adopter à lax promesse l'état de , sous l'hypothèse que x se comporte au moins un peu comme une promesse . Sinon, il remplit sa promesse avec la valeur x.

Ce traitement des éléments exploitables permet aux implémentations de promesses d'interopérer, tant qu'elles exposent une méthode conforme à Promises / A +. Il permet également aux implémentations Promises / A + d '«assimiler» les implémentations non conformes avec des méthodes raisonnables.

La chose clé à noter ici est cette ligne:

si xc'est une promesse, adoptez son état [3.4]

lien: https://promisesaplus.com/#point-49

Hrishi
la source
4
"Adopter son état" est un moyen concis et utile d'exprimer le comportement lorsqu'un thengestionnaire renvoie une promesse. +1 pour la référence de spécification.
69
En fait - la partie pertinente de la spécification ici est le fait que l' [[Resolve]]on appelle à la fois sur les thencapacités et sur les valeurs, donc elle englobe une valeur avec la promesse return "aaa"est donc la même que return Promise.resolve("aaa")et return Promise.resolve("aaa")est la même que return Promise.resolve(Promise.resolve("aaa"))- puisque la résolution est idempotente en l'appelant sur une valeur plus qu'une fois a le même résultat.
Benjamin Gruenbaum
8
@Benjamin Gruenbaum signifie-t-il que le retour "aaa"et return Promise.resolve("aaa")sont interchangeables dans les thencapacités dans tous les cas?
CSnerd
9
Oui, c'est exactement ce que cela signifie.
Benjamin Gruenbaum
118

En termes simples, à l'intérieur d'une thenfonction de gestionnaire:

A) Quand xest une valeur (nombre, chaîne, etc.):

  1. return x est équivalent à return Promise.resolve(x)
  2. throw x est équivalent à return Promise.reject(x)

B) Quand xune promesse est-elle déjà réglée (plus en attente):

  1. return xest équivalent à return Promise.resolve(x), si la Promesse a déjà été résolue.
  2. return xest équivalent à return Promise.reject(x), si la promesse a déjà été rejetée.

C) Quand xune promesse est-elle en attente:

  1. return xrenverra une promesse en attente, et elle sera évaluée lors de la suivante then.

En savoir plus sur ce sujet dans les documents Promise.prototype.then () .

Arian Acosta
la source
93

Vos deux exemples devraient se comporter à peu près de la même manière.

Une valeur renvoyée à l'intérieur d'un then()gestionnaire devient la valeur de résolution de la promesse retournée à partir de cela then(). Si la valeur renvoyée à l'intérieur de la .then est une promesse, la promesse retournée par then()"adoptera l'état" de cette promesse et résoudra / rejetera tout comme la promesse retournée.

Dans votre premier exemple, vous revenez "bbb"dans le premier then()gestionnaire, donc "bbb"est passé dans le then()gestionnaire suivant .

Dans votre deuxième exemple, vous renvoyez une promesse qui est immédiatement résolue avec la valeur "bbb"et "bbb"est donc transmise au then()gestionnaire suivant . (L' Promise.resolve()ici est étranger).

Le résultat est le même.

Si vous pouvez nous montrer un exemple qui présente en fait un comportement différent, nous pouvons vous expliquer pourquoi cela se produit.

JLRishe
la source
1
Bonne réponse! Et Promise.resolve();vs return;?
FabianTe
2
@FabianTe Ceux-ci auraient également le même effet, sauf avec undefinedau lieu de "bbb".
JLRishe
51

Vous avez déjà une bonne réponse formelle. J'ai pensé que je devrais en ajouter un court.

Les choses suivantes sont identiques aux promesses / promesses A + :

  • Appel Promise.resolve(dans votre cas angulaire c'est $q.when)
  • Appel du constructeur de promesses et résolution dans son résolveur. Dans votre cas, c'est new $q.
  • Renvoyer une valeur à partir d'un thenrappel.
  • Appelez Promise.all sur un tableau avec une valeur, puis extrayez cette valeur.

Ainsi, les éléments suivants sont tous identiques pour une promesse ou une valeur simple X:

Promise.resolve(x);
new Promise(function(resolve, reject){ resolve(x); });
Promise.resolve().then(function(){ return x; });
Promise.all([x]).then(function(arr){ return arr[0]; });

Et ce n'est pas surprenant, la spécification des promesses est basée sur la procédure de résolution des promesses qui permet une interaction facile entre les bibliothèques (comme $ q et les promesses natives) et vous facilite la vie dans l'ensemble. Chaque fois qu'une résolution de promesse peut se produire, une résolution se produit, créant une cohérence globale.

Benjamin Gruenbaum
la source
puis-je demander à quoi ça sert Promise.resolve().then(function(){ return x; });? J'ai trouvé un snipping faisant quelque chose de similaire (il a appelé une fonction à l'intérieur du thenbloc). Je pensais que c'était plus ou moins comme faire un timeout, mais c'est un peu plus rapide. jsben.ch/HIfDo
Sampgun
Il est inutile que ce soit la même chose que Promise.resolve (x) dans 99,99% des cas. (le 0.001% est que nous sommes dans un withbloc sur un objet ou un proxy avec un xaccesseur de propriété qui lève une exception. Dans ce cas, Promise.resolve (x) provoquerait une erreur levée mais Promise.resolve().then(function(){ return x; });serait une promesse rejetée puisque l'erreur est levée en a then).
Benjamin Gruenbaum
vous avez lié un blitz vide ou vous n'avez pas enregistré. Quoi qu'il en soit, je ne parlais pas des différences entre les déclarations. Je parlais précisément de ce que j'ai écrit. Pour être plus clair, voici l'extrait dont je parlais: if (validator) { Promise.resolve().then(() => { this._cdRef.markForCheck(); }); }. Ici, la promesse n'est pas attribuée, alors à quoi ça sert? Un timeout aurait (plus ou moins) le même effet, ou pas?
Sampgun
1
Il exécute l'appel de manière asynchrone une fois que tout le code synchrone s'est produit, mais avant toute E / S. C'est ce qu'on appelle la «sémantique microtick».
Benjamin Gruenbaum
1

La seule différence est que vous créez une promesse inutile lorsque vous le faites return Promise.resolve("bbb"). Le renvoi d'une promesse d'un onFulfilled()gestionnaire démarre la résolution de la promesse . Voilà comment fonctionne le chaînage des promesses .

vkarpov15
la source