Paso explícito
Similar a anidar las devoluciones de llamada, esta técnica se basa en cierres. Sin embargo, la cadena se mantiene plana: en lugar de pasar solo el último resultado, se pasa algún objeto de estado por cada paso. Estos objetos de estado acumulan los resultados de las acciones anteriores, transmitiendo todos los valores que se necesitarán más tarde más el resultado de la tarea actual.
function getExample() {
return promiseA(…).then(function(resultA) {
// some processing
return promiseB(…).then(b => [resultA, b]); // function(b) { return [resultA, b] }
}).then(function([resultA, resultB]) {
// more processing
return // something using both resultA and resultB
});
}
Aquí, esa pequeña flecha b => [resultA, b]
es la función que se cierra resultA
y pasa una matriz de ambos resultados al siguiente paso. Que utiliza la sintaxis de desestructuración de parámetros para dividirla en variables individuales nuevamente.
Antes de que la desestructuración estuviera disponible con ES6, .spread()
muchas bibliotecas prometedoras proporcionaron un ingenioso método auxiliar llamado ( Q , Bluebird , cuándo , ...). Se necesita una función con múltiples parámetros, uno para cada elemento de la matriz, para usarse como .spread(function(resultA, resultB) { …
.
Por supuesto, ese cierre necesario aquí puede simplificarse aún más mediante algunas funciones auxiliares, por ejemplo,
function addTo(x) {
// imagine complex `arguments` fiddling or anything that helps usability
// but you get the idea with this simple one:
return res => [x, res];
}
…
return promiseB(…).then(addTo(resultA));
Alternativamente, puede emplear Promise.all
para producir la promesa de la matriz:
function getExample() {
return promiseA(…).then(function(resultA) {
// some processing
return Promise.all([resultA, promiseB(…)]); // resultA will implicitly be wrapped
// as if passed to Promise.resolve()
}).then(function([resultA, resultB]) {
// more processing
return // something using both resultA and resultB
});
}
Y no solo puede usar matrices, sino también objetos complejos arbitrariamente. Por ejemplo, con _.extend
o Object.assign
en una función auxiliar diferente:
function augment(obj, name) {
return function (res) { var r = Object.assign({}, obj); r[name] = res; return r; };
}
function getExample() {
return promiseA(…).then(function(resultA) {
// some processing
return promiseB(…).then(augment({resultA}, "resultB"));
}).then(function(obj) {
// more processing
return // something using both obj.resultA and obj.resultB
});
}
Si bien este patrón garantiza una cadena plana y los objetos de estado explícito pueden mejorar la claridad, se volverá tedioso para una cadena larga. Especialmente cuando necesita el estado solo esporádicamente, todavía tiene que pasarlo por cada paso. Con esta interfaz fija, las devoluciones de llamada individuales en la cadena están bastante unidas e inflexibles para cambiar. Hace que la factorización de pasos individuales sea más difícil, y las devoluciones de llamada no se pueden suministrar directamente desde otros módulos; siempre deben incluirse en un código repetitivo que se preocupe por el estado. Funciones auxiliares abstractas como las anteriores pueden aliviar un poco el dolor, pero siempre estará presente.
javascript
, es relevante en otro idioma. Solo uso la respuesta "romper la cadena" en java y jdeferred