¿Cómo accedo a los resultados de promesas anteriores en una cadena .then()?
Reestructuré mi código en promesas y construí una maravillosa cadena de promesa larga y plana , que consta de múltiples .then()
devoluciones de llamada. Al final, quiero devolver algún valor compuesto y necesito acceder a múltiples resultados de promesa intermedios . Sin embargo, los valores de resolución de la mitad de la secuencia no están dentro del alcance de la última devolución de llamada, ¿cómo accedo a ellos?
function getExample() {
return promiseA(…).then(function(resultA) {
// Some processing
return promiseB(…);
}).then(function(resultB) {
// More processing
return // How do I gain access to resultA here?
});
}
Romper la cadena
Cuando necesite acceder a los valores intermedios de su cadena, debe dividirla en las piezas individuales que necesite. En lugar de adjuntar una devolución de llamada y de alguna manera intentar usar su parámetro varias veces, adjunte varias devoluciones de llamada a la misma promesa, siempre que necesite el valor del resultado. ¡No olvide que una promesa solo representa (representa) un valor futuro ! Además de derivar una promesa de otra en una cadena lineal, utilice los combinadores de promesas que le proporciona su biblioteca para generar el valor del resultado.
Esto dará como resultado un flujo de control muy sencillo, una composición clara de funcionalidades y, por lo tanto, una modularización sencilla.
function getExample() {
var a = promiseA(…);
var b = a.then(function(resultA) {
// some processing
return promiseB(…);
});
return Promise.all([a, b]).then(function([resultA, resultB]) {
// more processing
return // something using both resultA and resultB
});
}
En lugar de que la desestructuración de parámetros en la devolución de llamada después de Promise.all
eso solo estuviera disponible con ES6, en ES5 la then
llamada sería reemplazada por un ingenioso método auxiliar proporcionado por muchas bibliotecas de promesas ( Q , Bluebird , when ,…) .spread(function(resultA, resultB) { …
:
Bluebird también presenta una join
función dedicada para reemplazar esa combinación Promise.all
+ spread
con una construcción más simple (y más eficiente):
…
return Promise.join(a, b, function(resultA, resultB) { … });
Armonía ECMAScript
Por supuesto, este problema también fue reconocido por los diseñadores del lenguaje. Trabajaron mucho y la propuesta de funciones asíncronas finalmente se convirtió en realidad.
ECMAScript 8
Ya no necesita una sola then
invocación o función de devolución de llamada, ya que en una función asincrónica (que devuelve una promesa cuando se la llama) simplemente puede esperar a que las promesas se resuelvan directamente. También presenta estructuras de control arbitrarias como condiciones, bucles y cláusulas try-catch, pero por conveniencia no las necesitamos aquí:
async function getExample() {
var resultA = await promiseA(…);
// some processing
var resultB = await promiseB(…);
// more processing
return // something using both resultA and resultB
}
ECMAScript 6
Mientras esperábamos ES8, ya usábamos un tipo de sintaxis muy similar. ES6 vino con funciones de generador , que permiten dividir la ejecución en partes en yield
palabras clave colocadas arbitrariamente. Esos sectores se pueden ejecutar uno tras otro, de forma independiente e incluso de forma asincrónica, y eso es justo lo que hacemos cuando queremos esperar una resolución de promesa antes de ejecutar el siguiente paso.
Hay bibliotecas dedicadas (como co o task.js ), pero también muchas bibliotecas prometedoras tienen funciones auxiliares ( Q , Bluebird , when ,…) que realizan esta ejecución asíncrona paso a paso cuando les asignas una función generadora que rinde promesas.
var getExample = Promise.coroutine(function* () {
// ^^^^^^^^^^^^^^^^^ Bluebird syntax
var resultA = yield promiseA(…);
// some processing
var resultB = yield promiseB(…);
// more processing
return // something using both resultA and resultB
});
Esto funcionó en Node.js desde la versión 4.0, además algunos navegadores (o sus ediciones de desarrollo) admitieron la sintaxis del generador relativamente temprano.
ECMAScript 5
Sin embargo, si desea o necesita ser compatible con versiones anteriores, no puede utilizarlos sin un transpilador. Tanto las funciones de generador como las funciones asíncronas son compatibles con las herramientas actuales; consulte, por ejemplo, la documentación de Babel sobre generadores y funciones asíncronas .
Y luego, también hay muchos otros lenguajes de compilación en JS
que se dedican a facilitar la programación asincrónica. Generalmente usan una sintaxis similar a await
(por ejemplo, Iced CoffeeScript ), pero también hay otros que presentan una do
notación similar a Haskell (por ejemplo , LatteJs , monádico , PureScript o LispyScript ).
Inspección sincrónica
Asignar promesas de valores necesarios más adelante a variables y luego obtener su valor mediante inspección sincrónica. El ejemplo utiliza el método de bluebird .value()
, pero muchas bibliotecas proporcionan un método similar.
function getExample() {
var a = promiseA(…);
return a.then(function() {
// some processing
return promiseB(…);
}).then(function(resultB) {
// a is guaranteed to be fulfilled here so we can just retrieve its
// value synchronously
var aValue = a.value();
});
}
Esto se puede utilizar para tantos valores como desee:
function getExample() {
var a = promiseA(…);
var b = a.then(function() {
return promiseB(…)
});
var c = b.then(function() {
return promiseC(…);
});
var d = c.then(function() {
return promiseD(…);
});
return d.then(function() {
return a.value() + b.value() + c.value() + d.value();
});
}