Pass-through esplicito
Simile alla nidificazione dei callback, questa tecnica si basa su chiusure. Tuttavia, la catena rimane piatta - invece di passare solo il risultato più recente, viene passato un oggetto stato per ogni passaggio. Questi oggetti stato accumulano i risultati delle azioni precedenti, tramandando di nuovo tutti i valori che saranno necessari in seguito più il risultato dell'attività corrente.
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
});
}
Qui, quella piccola freccia b => [resultA, b]
è la funzione che si chiude resultA
e passa una matrice di entrambi i risultati al passaggio successivo. Che utilizza la sintassi destrutturante dei parametri per suddividerla nuovamente in singole variabili.
Prima che la destrutturazione diventasse disponibile con ES6, un ingegnoso metodo di supporto chiamato .spread()
era fornito da molte librerie promesse ( Q , Bluebird , quando , ...). Ci vuole una funzione con più parametri - uno per ogni elemento dell'array - da usare come .spread(function(resultA, resultB) { …
.
Naturalmente, la chiusura necessaria qui può essere ulteriormente semplificata da alcune funzioni di supporto, ad es
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));
In alternativa, è possibile utilizzare Promise.all
per produrre la promessa per l'array:
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
});
}
E potresti non solo usare array, ma oggetti arbitrariamente complessi. Ad esempio, con _.extend
o Object.assign
in un'altra funzione di supporto:
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
});
}
Mentre questo modello garantisce una catena piatta e oggetti di stato espliciti possono migliorare la chiarezza, diventerà noioso per una lunga catena. Soprattutto quando hai bisogno dello stato solo sporadicamente, devi comunque passarlo attraverso ogni passo. Con questa interfaccia fissa, i singoli callback nella catena sono piuttosto strettamente accoppiati e non flessibili da modificare. Rende più difficile il factoring di singoli passaggi e i callback non possono essere forniti direttamente da altri moduli: devono sempre essere racchiusi nel codice del boilerplate che si preoccupa dello stato. Funzioni di supporto astratte come quelle sopra possono alleviare un po 'il dolore, ma sarà sempre presente.
javascript
, è rilevante in un'altra lingua. Uso solo la risposta "rompi la catena" in java e jdeferred