Promise.resolve vs new Promise (resolver)

94

Estoy usando bluebird y veo dos formas de resolver funciones sincrónicas en una Promise, pero no entiendo las diferencias entre ambas formas. Parece que el stacktrace es un poco diferente, por lo que no son solo un alias, ¿verdad?

Entonces, ¿cuál es la forma preferida?

Camino A

function someFunction(someObject) {
  return new Promise(function(resolve) {
    someObject.resolved = true;
    resolve(someObject);
  });
}

Camino B

function someFunction(someObject) {
  someObject.resolved = true;
  return Promise.resolve(someObject);
}
Pipo
fuente
3
Promise.resolvees solo azúcar.
Qantas 94 Heavy
1
Respuesta corta: sin diferencias de uso. Solo azúcar.
Pinal
@Pinal ¿Qué es "azúcar"?
doubleOrt
5
@Tauro. El azúcar sintáctico es una sintaxis diseñada para facilitar la lectura o expresión. ver: wikipedia .
Wyck

Respuestas:

82

Al contrario de ambas respuestas en los comentarios, hay una diferencia.

Mientras

Promise.resolve(x);

es básicamente lo mismo que

new Promise(function(r){ r(x); });

hay una sutileza.

Las funciones de devolución de promesas generalmente deben tener la garantía de que no deben lanzar de forma síncrona, ya que pueden lanzar de forma asincrónica. Para evitar resultados inesperados y condiciones de carrera, los lanzamientos generalmente se convierten en rechazos devueltos.

Con esto en mente, cuando se creó la especificación, el constructor de la promesa es seguro.

¿Y si someObjectes undefined?

  • Way A devuelve una promesa rechazada.
  • Way B lanza sincrónicamente.

Bluebird vio esto y Petka agregó Promise.methodpara abordar este problema para que pueda seguir usando valores de retorno. Entonces, la forma correcta y más fácil de escribir esto en Bluebird es en realidad ninguna de las dos, es:

var someFunction = Promise.method(function someFunction(someObject){
    someObject.resolved = true;
    return someObject;
});

Promise.method convertirá los lanzamientos en rechazos y los retornos a resoluciones por ti. Es la forma más segura de hacer esto y asimila thenables a través de valores de retorno, por lo que funcionaría incluso si someObjectde hecho es una promesa.

En general, Promise.resolvese utiliza para convertir objetos y promesas extranjeras (thenables) en promesas. Ese es su caso de uso.

Benjamin Gruenbaum
fuente
"Las funciones de devolución de promesas deberían tener la garantía de que no deberían lanzar sincrónicamente, ya que podrían lanzar de forma asincrónica". ¿Podría explicar por qué las funciones deberían ser sincrónicas o asincrónicas, pero no ambas? Actualmente disfruto de Promise.resolve (), ¿irías tan lejos como para decir que usar Promise.resolve()es un anti-patrón?
Ashley Coolman
2
@AshleyCoolman consulte blog.izs.me/post/59142742143/designing-apis-for-asynchrony : un método que a veces se comporta de forma asincrónica siempre debe hacerlo para mantener la coherencia.
Benjamin Gruenbaum
¿ Promise.resolve()Crea una nueva instancia de Promisela misma forma que usa new? Si no, return Promise.resolve(yourCode)sería más rápido y evitaría los tiros sincrónicos.
Steven Vachon
1
Me siento mal, utilizo "Promise.resolve (). Then (function () {/ * caso que puede arrojar un error * /}). Then ..." para asegurarme de que el error se convierta en una promesa rechazada ... Buscaré más en el "método Promise.method"
Polopollo
1
@Polopollo o Promise.coroutinecuál es aún más útil.
Benjamin Gruenbaum
16

Hay otra diferencia que no se menciona en las respuestas o comentarios anteriores:

Si someObjectes un Promise, new Promise(resolve)costaría dos tick adicionales.


Compare dos fragmentos de código siguientes:

const p = new Promise(resovle => setTimeout(resovle));

new Promise(resolve => resolve(p)).then(() => {
  console.log("tick 3");
});

p.then(() => {
  console.log("tick 1");
}).then(() => {
  console.log("tick 2");
});

const p = new Promise(resovle => setTimeout(resovle));

Promise.resolve(p).then(() => {
  console.log("tick 3");
});

p.then(() => {
  console.log("tick 1");
}).then(() => {
  console.log("tick 2");
});

El segundo fragmento imprimiría "tick 3" en primer lugar. ¿Por qué?

  • Si el valor es una promesa, Promise.resolve(value)devolvería el valor exactamente. Promise.resolve(value) === valuesería cierto. ver MDN

  • Pero new Promise(resolve => resolve(value))devolvería una nueva promesa que se ha cerrado para seguir la valuepromesa. Se necesita una marca adicional para hacer el "bloqueo".

    // something like:
    addToMicroTaskQueue(() => {
      p.then(() => {
        /* resolve newly promise */
      })
        // all subsequent .then on newly promise go on from here
        .then(() => {
          console.log("tick 3");
        });
    });

    La tick 1 .thenllamada se ejecutaría primero.


Referencias:

edvard chen
fuente