Jaka jest wyraźna obietnica konstrukcji przeciwwzorcowej i jak jej uniknąć?
Pisałem kod, który robi coś takiego:
function getStuffDone(param) { | function getStuffDone(param) {
var d = Q.defer(); /* or $q.defer */ | return new Promise(function(resolve, reject) {
// or = new $.Deferred() etc. | // using a promise constructor
myPromiseFn(param+1) | myPromiseFn(param+1)
.then(function(val) { /* or .done */ | .then(function(val) {
d.resolve(val); | resolve(val);
}).catch(function(err) { /* .fail */ | }).catch(function(err) {
d.reject(err); | reject(err);
}); | });
return d.promise; /* or promise() */ | });
} | }
Ktoś mi powiedział, że nazywa się to odpowiednio „ odroczonym antywzorem ” lub „ Promise
antywzorem konstruktora ”. Co jest złego w tym kodzie i dlaczego nazywa się to antywzorem ?
Odpowiedzi
Odroczone antywzorzec projektowy (obecnie wyraźne-budowa antywzorzec projektowy) wymyślone przez Esailija jest wspólne antywzorzec projektowy ludzi, którzy są nowicjuszami w obietnice zrobić, zrobiłem to sam, kiedy po raz pierwszy użyty obietnic. Problem z powyższym kodem polega na tym, że nie wykorzystuje faktu, że łańcuch obiecuje.
Obietnice można łączyć z obietnicami .then
i można je bezpośrednio zwracać. Twój kod w getStuffDone
można przepisać jako:
function getStuffDone(param){
return myPromiseFn(param+1); // much nicer, right?
}
Obietnice dotyczą zwiększenia czytelności kodu asynchronicznego i zachowania się jak kod synchroniczny bez ukrywania tego faktu. Obietnice reprezentują abstrakcję wartości jednorazowej operacji, abstrakcję stanowią pojęcie instrukcji lub wyrażenia w języku programowania.
Odroczonych obiektów należy używać tylko wtedy, gdy konwertujesz interfejs API na obietnice i nie możesz tego zrobić automatycznie, lub gdy piszesz funkcje agregacji, które można łatwiej wyrazić w ten sposób.
Cytując Esailija:
Jest to najczęstszy anty-wzór. Łatwo się w to wpaść, gdy tak naprawdę nie rozumiesz obietnic i myślisz o nich jako o chwalebnych emiterach zdarzeń lub narzędziu wywołania zwrotnego. Podsumujmy: obietnice dotyczą zachowania większości utraconych właściwości kodu synchronicznego w kodzie asynchronicznym, takich jak płaskie wcięcia i jeden kanał wyjątków.
Co jest z tym nie tak?
Ale wzór działa!
Szczęściarz. Niestety, prawdopodobnie tak nie jest, ponieważ prawdopodobnie zapomniałeś o jakimś skrajnym przypadku. W ponad połowie przypadków, które widziałem, autor zapomniał zająć się obsługą błędów:
return new Promise(function(resolve) {
getOtherPromise().then(function(result) {
resolve(result.property.example);
});
})
Jeśli druga obietnica zostanie odrzucona, stanie się to niezauważone, a nie zostanie przekazane do nowej obietnicy (gdzie zostanie zrealizowana) - a nowa obietnica pozostaje na zawsze w toku, co może powodować przecieki.
To samo dzieje się w przypadku, gdy Twój kod wywołania zwrotnego powoduje błąd - np. Gdy result
nie ma a property
i zostanie zgłoszony wyjątek. To byłoby nierozwiązane i pozostawiłoby nową obietnicę nierozwiązaną.
W przeciwieństwie do tego, używanie .then()
automatycznie obsługuje oba te scenariusze i odrzuca nową obietnicę, gdy wystąpi błąd:
return getOtherPromise().then(function(result) {
return result.property.example;
})
Odroczony antywzór jest nie tylko uciążliwy, ale także podatny na błędy . Używanie .then()
do łączenia jest znacznie bezpieczniejsze.
Ale ja załatwiłem wszystko!
Naprawdę? Dobry. Jednak będzie to dość szczegółowe i obszerne, zwłaszcza jeśli używasz biblioteki obietnic, która obsługuje inne funkcje, takie jak anulowanie lub przekazywanie wiadomości. A może w przyszłości tak się stanie, a może chcesz zamienić swoją bibliotekę na lepszą? Nie będziesz chciał przepisać w tym celu swojego kodu.
Metody bibliotek ( then
) nie tylko natywnie obsługują wszystkie funkcje, ale mogą również mieć pewne optymalizacje. Korzystanie z nich prawdopodobnie przyspieszy kod lub przynajmniej pozwoli na jego optymalizację przez przyszłe wersje biblioteki.
Jak tego uniknąć?
Jeśli więc zauważysz, że ręcznie tworzysz obietnice Promise
lub Deferred
i masz już istniejące obietnice, najpierw sprawdź interfejs API biblioteki . Odroczony antywzór jest często stosowany przez ludzi, którzy postrzegają obietnice [tylko] jako wzorzec obserwatora - ale obietnice to coś więcej niż wywołania zwrotne : mają być kompozycyjne. Każda przyzwoita biblioteka ma wiele łatwych w użyciu funkcji do komponowania obietnic w każdy możliwy do wyobrażenia sposób, zajmujących się wszystkimi niskopoziomowymi rzeczami, którymi nie chcesz się zajmować.
Jeśli zauważyłeś potrzebę skomponowania niektórych obietnic w nowy sposób, który nie jest obsługiwany przez istniejącą funkcję pomocniczą, napisanie własnej funkcji z nieuniknionymi Odroczeniami powinno być ostatnią opcją. Rozważ przejście na bardziej funkcjonalną bibliotekę i / lub zgłoś błąd w swojej obecnej bibliotece. Jego opiekun powinien być w stanie wyprowadzić kompozycję z istniejących funkcji, zaimplementować nową funkcję pomocniczą i / lub pomóc zidentyfikować przypadki skrajne, które należy obsłużyć.