¿Cómo accedo a los resultados de promesas anteriores en una cadena .then()?

Resuelto Bergi asked hace 9 años • 17 respuestas

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?
    });
}
Bergi avatar Jan 31 '15 17:01 Bergi
Aceptado

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.alleso solo estuviera disponible con ES6, en ES5 la thenllamada 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 joinfunción dedicada para reemplazar esa combinación Promise.all+ spreadcon una construcción más simple (y más eficiente):

return Promise.join(a, b, function(resultA, resultB) { … });
Bergi avatar Jan 31 '2015 10:01 Bergi

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 theninvocació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 yieldpalabras 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 donotación similar a Haskell (por ejemplo , LatteJs , monádico , PureScript o LispyScript ).

Bergi avatar Jan 31 '2015 10:01 Bergi

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();
    });
}
Esailija avatar Jan 31 '2015 13:01 Esailija