w3schools promises nodejs google ejemplo javascript node.js promise es6-promise

javascript - promises - Callbacks de promesas devolviendo promesas



promises javascript ajax (4)

En este ejemplo, p2 es una promesa. p3 también es una promesa que se origina en el controlador de cumplimiento de p1. Sin embargo, p2! == p3. En cambio, p2 de alguna manera se resuelve mágicamente a 43 (¿cómo?) Y ese valor se pasa al controlador de cumplimiento de p3. Incluso la oración aquí es confusa.

una versión simplificada de cómo funciona esto (solo pseudocódigo)

function resolve(value){ if(isPromise(value)){ value.then(resolve, reject); }else{ //dispatch the value to the listener } }

Todo esto es bastante más complicado ya que hay que tener cuidado, ya sea que la promesa ya se haya resuelto y algunas cosas más.

Con respecto a estas dos grandes fuentes: NZakas: Devolviendo promesas en Promise Chains y MDN Promises , me gustaría preguntar lo siguiente:

Cada vez que devolvemos un valor de un controlador de cumplimiento de promesa, ¿cómo se transfiere ese valor a la nueva promesa del mismo controlador?

Por ejemplo,

let p1 = new Promise(function(resolve, reject) { resolve(42); }); let p2 = new Promise(function(resolve, reject) { resolve(43); }); let p3 = p1.then(function(value) { // first fulfillment handler console.log(value); // 42 return p2; }); p3.then(function(value) { // second fulfillment handler console.log(value); // 43 });

En este ejemplo, p2 es una promesa. p3 también es una promesa que se origina en el controlador de cumplimiento de p1 . Sin embargo, p2 !== p3 . En cambio, p2 alguna manera se resuelve mágicamente a 43 (¿cómo?) Y ese valor se pasa al controlador de cumplimiento de p3 . Incluso la oración aquí es confusa.

¿Podría explicarme qué está sucediendo exactamente aquí? Estoy totalmente confundido sobre este concepto.


Básicamente, p3 está return una promesa más: p2 . Lo que significa que el resultado de p2 pasará como parámetro a la siguiente devolución de llamada, en este caso se resuelve en 43 .

Cada vez que utiliza la palabra clave return , pasa el resultado como parámetro a la siguiente devolución de llamada.

let p3 = p1.then(function(value) { // first fulfillment handler console.log(value); // 42 return p2; });

Tu codigo :

p3.then(function(value) { // second fulfillment handler console.log(value); // 43 });

Es igual a:

p1.then(function(resultOfP1) { // resultOfP1 === 42 return p2; // // Returning a promise ( that might resolve to 43 or fail ) }) .then(function(resultOfP2) { console.log(resultOfP2) // ''43'' });

Por cierto, me di cuenta de que está utilizando la sintaxis ES6, puede tener una sintaxis más clara utilizando la sintaxis de flecha gruesa:

p1.then(resultOfP1 => p2) // the `return` is implied since it''s a one-liner .then(resultOfP2 => console.log(resultOfP2));


Digamos que arrojar dentro de la devolución de llamada then() rechaza la promesa de resultado con un error, y regresar desde then() devolución de llamada then() cumple la promesa de resultado con un valor de éxito.

let p2 = p1.then(() => { throw new Error(''lol'') }) // p2 was rejected with Error(''lol'') let p3 = p1.then(() => { return 42 }) // p3 was fulfilled with 42

Pero a veces, incluso dentro de la continuación, no sabemos si hemos tenido éxito o no. Necesitamos mas tiempo.

return checkCache().then(cachedValue => { if (cachedValue) { return cachedValue } // I want to do some async work here })

Sin embargo, si hago un trabajo asíncrono allí, sería demasiado tarde para return o throw , ¿no?

return checkCache().then(cachedValue => { if (cachedValue) { return cachedValue } fetchData().then(fetchedValue => { // Doesn’t make sense: it’s too late to return from outer function by now. // What do we do? // return fetchedValue }) })

Es por eso que Promesas no sería útil si no pudieras resolver otra Promesa .

No significa que, en su ejemplo, p2 se convierta en p3 . Son objetos separados de Promise. Sin embargo, al devolver p2 partir de then() que produce p3 , está diciendo "Quiero que p3 resuelva a lo que p2 resuelva, ya sea que tenga éxito o no".

En cuanto a cómo sucede esto, es específico de la implementación. Internamente, puede pensar en then() como la creación de una nueva Promesa. La implementación podrá cumplirlo o rechazarlo cuando lo desee. Normalmente, lo cumplirá o rechazará automáticamente cuando regrese:

// Warning: this is just an illustration // and not a real implementation code. // For example, it completely ignores // the second then() argument for clarity, // and completely ignores the Promises/A+ // requirement that continuations are // run asynchronously. then(callback) { // Save these so we can manipulate // the returned Promise when we are ready let resolve, reject // Imagine this._onFulfilled is an internal // queue of code to run after current Promise resolves. this._onFulfilled.push(() => { let result, error, succeeded try { // Call your callback! result = callback(this._result) succeeded = true } catch (err) { error = err succeeded = false } if (succeeded) { // If your callback returned a value, // fulfill the returned Promise to it resolve(result) } else { // If your callback threw an error, // reject the returned Promise with it reject(error) } }) // then() returns a Promise return new Promise((_resolve, _reject) => { resolve = _resolve reject = _reject }) }

Nuevamente, esto es en gran medida un seudocódigo, pero muestra la idea detrás de cómo then() podría implementar then() en las implementaciones de Promise.

Si queremos agregar soporte para resolver una Promesa, solo tenemos que modificar el código para tener una rama especial si la callback que pasa then() devolvió una Promesa:

if (succeeded) { // If your callback returned a value, // resolve the returned Promise to it... if (typeof result.then === ''function'') { // ...unless it is a Promise itself, // in which case we just pass our internal // resolve and reject to then() of that Promise result.then(resolve, reject) } else { resolve(result) } } else { // If your callback threw an error, // reject the returned Promise with it reject(error) } })

Permítanme aclarar nuevamente que esta no es una implementación real de Promise y tiene grandes agujeros e incompatibilidades. Sin embargo, debería darle una idea intuitiva de cómo las bibliotecas de Promise implementan la resolución de una Promesa. Después de que se sienta cómodo con la idea, le recomendaría que eche un vistazo a cómo las implementaciones reales de Promise manejan esto .


Trataré de responder la pregunta "¿por qué then devoluciones de llamada pueden devolver las Promise mismas" más canónicas. Para tomar un ángulo diferente, comparo Promise s con un tipo de contenedor menos complejo y confuso: Array s.

Una Promise es un contenedor para un valor futuro. Una Array es un contenedor para un número arbitrario de valores.

No podemos aplicar funciones normales a los tipos de contenedores:

const sqr = x => x * x; const xs = [1,2,3]; const p = Promise.resolve(3); sqr(xs); // fails sqr(p); // fails

Necesitamos un mecanismo para elevarlos al contexto de un contenedor específico:

xs.map(sqr); // [1,4,9] p.then(sqr); // Promise {[[PromiseValue]]: 9}

Pero, ¿qué sucede cuando la función proporcionada en sí misma devuelve un contenedor del mismo tipo?

const sqra = x => [x * x]; const sqrp = x => Promise.resolve(x * x); const xs = [1,2,3]; const p = Promise.resolve(3); xs.map(sqra); // [[1],[4],[9]] p.then(sqrp); // Promise {[[PromiseValue]]: 9}

sqra actúa como se esperaba. Simplemente devuelve un contenedor anidado con los valores correctos. Sin embargo, esto obviamente no es muy útil.

Pero, ¿cómo se puede interpretar el resultado de sqrp ? Si seguimos nuestra propia lógica, tenía que ser algo así como Promise {[[PromiseValue]]: Promise {[[PromiseValue]]: 9}} , pero no lo es. Entonces, ¿qué magia está pasando aquí?

Para reconstruir el mecanismo simplemente necesitamos adaptar un poco nuestro método de map :

const flatten = f => x => f(x)[0]; const sqra = x => [x * x]; const sqrp = x => Promise.resolve(x * x); const xs = [1,2,3]; xs.map(flatten(sqra))

flatten solo toma una función y un valor, aplica la función al valor y desenvuelve el resultado, por lo que reduce una estructura de matriz anidada en un nivel.

En pocas palabras, en el contexto de Promise s es equivalente a map combinado con flatten en el contexto de Array s. Este comportamiento es extremadamente importante. Podemos aplicar no solo funciones normales a una Promise sino también funciones que en sí mismas devuelven una Promise .

De hecho, este es el dominio de la programación funcional. Una Promise es una implementación específica de una mónada , then es bind / chain y una función que devuelve una Promise es una función monádica. Cuando comprendes la API de Promise , básicamente entiendes a todas las mónadas.