Jawne przekazywanie
Podobnie jak zagnieżdżanie wywołań zwrotnych, technika ta opiera się na zamknięciach. Łańcuch pozostaje jednak płaski - zamiast przekazywać tylko najnowszy wynik, dla każdego kroku przekazywany jest jakiś obiekt stanu. Te obiekty stanu gromadzą wyniki poprzednich działań, przekazując wszystkie wartości, które będą potrzebne później, plus wynik bieżącego zadania.
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
});
}
Tutaj ta mała strzałka b => [resultA, b]
jest funkcją, która zamyka się resultA
i przekazuje tablicę obu wyników do następnego kroku. Który używa składni destrukcyjnej parametru, aby ponownie podzielić ją na pojedyncze zmienne.
Zanim destrukcja stała się dostępna w ES6, .spread()
wiele obiecujących bibliotek ( Q , Bluebird , kiedy …) zapewniało sprytną metodę pomocniczą, nazywaną Pobiera funkcję z wieloma parametrami - po jednym dla każdego elementu tablicy - do użycia jako .spread(function(resultA, resultB) { …
.
Oczywiście to konieczne zamknięcie można dodatkowo uprościć dzięki niektórym funkcjom pomocniczym, np
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));
Alternatywnie możesz zastosować Promise.all
obietnicę dla tablicy:
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
});
}
I możesz nie tylko używać tablic, ale dowolnie złożonych obiektów. Na przykład z _.extend
lub Object.assign
w innej funkcji pomocniczej:
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
});
}
Chociaż ten wzór gwarantuje płaski łańcuch, a jawne obiekty stanu mogą poprawić przejrzystość, stanie się nudny dla długiego łańcucha. Zwłaszcza, gdy potrzebujesz stanu sporadycznie, wciąż musisz przejść go przez każdy krok. Dzięki temu stałemu interfejsowi pojedyncze wywołania zwrotne w łańcuchu są raczej ściśle powiązane i nieelastyczne do zmiany. Utrudnia faktoring pojedynczych kroków, a wywołania zwrotne nie mogą być dostarczane bezpośrednio z innych modułów - zawsze muszą być owinięte kodem typu „kocioł”, który dba o stan. Funkcje abstrakcyjnego pomocnika takie jak powyżej mogą nieco złagodzić ból, ale zawsze będą obecne.
javascript
, ma znaczenie w innym języku. Po prostu używam odpowiedzi „przerwać łańcuch” w java i jdeferred