¿Cuál es la diferencia entre el valor de retorno o Promise.resolve desde entonces ()

314

Cuál es la diferencia entre:

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

y esto:

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

Pregunto ya que obtengo un comportamiento diferente Uso del servicio Angular y $ http con el encadenamiento .then (). Demasiado código, por lo tanto, primero el ejemplo anterior.

spirytus
fuente
1
¿Qué "comportamiento diferente" estás viendo? Ambos ejemplos deberían funcionar y comportarse aproximadamente igual. El Promise.resolve()en el segundo ejemplo es innecesario.
JLRishe
44
@pixelbits No hay nada de malo en devolver una promesa de un thencontrolador, de hecho, es un aspecto clave de la especificación de promesas que puede hacer eso.
Tenga en cuenta que esto funciona con thens anidados arbitrariamente : el término 'otros idiomas' para esto es que thenes a mapy a flatMap.
Benjamin Gruenbaum
1
en la línea 2, ¿por qué tiene que llamar a res ("aaa"), por qué no puede devolver "aaa" es suficiente y la promesa lo atrapa para resolverlo () de la misma manera que detecta excepciones para rechazar ()?
Sam Liddicott
1
@SamLiddicott tiene la misma pregunta, mientras que las minas son un poco más complicadas: new Promise((res, rej) => { return fetch('//google.com').then(() => { return "haha"; }) }).then((result) => alert(result));este código simplemente se bloqueará (no se resolverá para siempre). Pero si cambio return "haha";a, return res("haha");entonces funcionará y alertará a "jaja". ¿El fetch (). Then () ya envolvió "jaja" en una promesa resuelta?
Shaung Cheng

Respuestas:

138

La regla es, si la función que está en el thencontrolador devuelve un valor, la promesa se resuelve / rechaza con ese valor, y si la función devuelve una promesa, lo que sucede es que la siguiente thencláusula será la thencláusula de la promesa que la función devolvió , por lo tanto, en este caso, el primer ejemplo cae a través de la secuencia normal de los thensvalores e imprime como se podría esperar, en el segundo ejemplo, el objeto de promesa que se devuelve cuando lo hace Promise.resolve("bbb")es el thenque se invoca al encadenar (para todos los efectos). La forma en que realmente funciona se describe a continuación con más detalle.

Citando de las promesas / especificaciones A +:

El procedimiento de resolución de promesas es una operación abstracta que toma como entrada una promesa y un valor, que denotamos como [[Resolve]](promise, x). Si xes posible, intenta hacer que la promesa adopte el estado dex , bajo el supuesto de que x se comporta al menos como una promesa . De lo contrario, cumple la promesa con el valor x.

Este tratamiento de los elementos nobles permite que las implementaciones de promesas interoperen, siempre que expongan un método compatible con Promesas / A +. También permite que las implementaciones Promises / A + "asimilen" implementaciones no conformes con métodos razonables.

La clave para notar aquí es esta línea:

si xes una promesa, adopte su estado [3.4]

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

Hrishi
fuente
44
"Adoptar su estado" es una forma concisa y útil de expresar el comportamiento cuando un thencontrolador devuelve una promesa. +1 para la referencia de especificaciones.
69
En realidad, la parte relevante de la especificación aquí es el hecho de que [[Resolve]]se llama tanto en thenvalores como en valores, por lo que esencialmente envuelve un valor con la promesa, por lo que return "aaa"es el mismo return Promise.resolve("aaa")y return Promise.resolve("aaa")es el mismo return Promise.resolve(Promise.resolve("aaa")), ya que la resolución es idempotente al llamarlo a un valor más que una vez tiene el mismo resultado.
Benjamin Gruenbaum
8
@Benjamin Gruenbaum, ¿significa eso retorno "aaa"y return Promise.resolve("aaa")son intercambiables en thenables en cualquier caso?
CSnerd
99
Sí, eso es exactamente lo que significa.
Benjamin Gruenbaum
118

En términos simples, dentro de una thenfunción de controlador:

A) Cuando xes un valor (número, cadena, etc.):

  1. return x es equivalente a return Promise.resolve(x)
  2. throw x es equivalente a return Promise.reject(x)

B) Cuándo xes una promesa que ya está liquidada (ya no está pendiente):

  1. return xes equivalente a return Promise.resolve(x), si la promesa ya se resolvió.
  2. return xes equivalente a return Promise.reject(x), si la promesa ya fue rechazada.

C) Cuando xhay una promesa pendiente:

  1. return xdevolverá una Promesa pendiente y se evaluará en el siguiente then.

Lea más sobre este tema en los documentos Promise.prototype.then () .

Arian Acosta
fuente
93

Ambos ejemplos deberían comportarse más o menos igual.

Un valor devuelto dentro de un then()controlador se convierte en el valor de resolución de la promesa devuelta de eso then(). Si el valor devuelto dentro del .then es una promesa, la promesa devuelta por then()"adoptará el estado" de esa promesa y se resolverá / rechazará tal como lo hace la promesa devuelta.

En su primer ejemplo, regresa "bbb"en el primer then()controlador, por lo que "bbb"se pasa al siguiente then()controlador.

En su segundo ejemplo, devuelve una promesa que se resuelve de inmediato con el valor "bbb", por lo que "bbb"se pasa al siguiente then()controlador. (El Promise.resolve()aquí es extraño).

El resultado es el mismo.

Si puede mostrarnos un ejemplo que en realidad exhibe un comportamiento diferente, podemos decirle por qué sucede eso.

JLRishe
fuente
1
¡Buena respuesta! ¿Qué hay de Promise.resolve();vs return;?
FabianTe
2
@FabianTe Esos también tendrían el mismo efecto, excepto con en undefinedlugar de "bbb".
JLRishe
51

Ya tienes una buena respuesta formal. Pensé que debería agregar uno corto.

Las siguientes cosas son idénticas a las promesas / promesas A + :

  • Llamando Promise.resolve(en su caso angular, eso es $q.when)
  • Llamando al constructor de la promesa y resolviendo en su resolutor. En tu caso eso es new $q.
  • Devolver un valor de una thendevolución de llamada.
  • Llamar a Promise.all en una matriz con un valor y luego extraer ese valor.

Por lo tanto, los siguientes son idénticos para una promesa o un valor 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]; });

Y no es sorprendente, la especificación de promesas se basa en el Procedimiento de resolución de promesas que permite una fácil interoperación entre bibliotecas (como $ q y promesas nativas) y hace que su vida sea más fácil en general. Cada vez que se produce una resolución prometedora, se produce una resolución que crea coherencia general.

Benjamin Gruenbaum
fuente
¿Puedo preguntar qué sentido tiene hacer Promise.resolve().then(function(){ return x; });? Encontré un recorte haciendo algo similar (llamó una función dentro del thenbloque). Pensé que era más o menos como hacer un tiempo de espera, pero es un poco más rápido. jsben.ch/HIfDo
Sampgun
No tiene sentido que sea lo mismo que Promise.resolve (x) en el 99.99% de los casos. (el 0.001% es que estamos en un withbloque sobre un objeto o proxy con un xdescriptor de acceso de propiedad que arroja una excepción. En ese caso, Promise.resolve (x) causaría un error arrojado pero Promise.resolve().then(function(){ return x; });sería una promesa rechazada ya que el error es arrojado en a then).
Benjamin Gruenbaum
vinculaste un bombardeo vacío o no lo guardaste. De todos modos, no estaba hablando de las diferencias entre las declaraciones. Estaba hablando precisamente de lo que escribí. Sólo para ser más claro, este es el fragmento que estaba hablando: if (validator) { Promise.resolve().then(() => { this._cdRef.markForCheck(); }); }. Aquí la promesa no está asignada, entonces, ¿cuál es el punto? Un tiempo de espera tendría (más o menos) el mismo efecto, ¿o no?
Sampgun
1
Realiza la llamada de forma asincrónica después de que todo el código síncrono haya sucedido pero antes de que ocurra cualquier E / S. Eso se llama "semántica de microtick".
Benjamin Gruenbaum