Podsumowanie: Czy istnieją pewne sprawdzone wzorce najlepszych praktyk, które mogę stosować, aby mój kod był czytelny pomimo korzystania z kodu asynchronicznego i funkcji zwrotnych?
Korzystam z biblioteki JavaScript, która wykonuje wiele czynności asynchronicznie i w dużej mierze opiera się na wywołaniach zwrotnych. Wydaje się, że napisanie prostej metody „load A, load B, ...” staje się dość skomplikowane i trudne do naśladowania przy użyciu tego wzorca.
Dam (wymyślony) przykład. Powiedzmy, że chcę załadować kilka obrazów (asynchronicznie) ze zdalnego serwera WWW. W C # / async napisałbym coś takiego:
disableStartButton();
foreach (myData in myRepository) {
var result = await LoadImageAsync("http://my/server/GetImage?" + myData.Id);
if (result.Success) {
myData.Image = result.Data;
} else {
write("error loading Image " + myData.Id);
return;
}
}
write("success");
enableStartButton();
Układ kodu przebiega zgodnie z „przebiegiem zdarzeń”: najpierw przycisk uruchamiania jest wyłączony, następnie obrazy są ładowane ( await
zapewnia, że interfejs użytkownika pozostaje responsywny), a następnie przycisk uruchamiania jest ponownie włączony.
W JavaScript, używając wywołań zwrotnych, wymyśliłem to:
disableStartButton();
var count = myRepository.length;
function loadImage(i) {
if (i >= count) {
write("success");
enableStartButton();
return;
}
myData = myRepository[i];
LoadImageAsync("http://my/server/GetImage?" + myData.Id,
function(success, data) {
if (success) {
myData.Image = data;
} else {
write("error loading image " + myData.Id);
return;
}
loadImage(i+1);
}
);
}
loadImage(0);
Myślę, że wady są oczywiste: musiałem przerobić pętlę na wywołanie rekurencyjne, kod, który powinien zostać wykonany na końcu, znajduje się gdzieś w środku funkcji, kod rozpoczynający pobieranie ( loadImage(0)
) znajduje się na samym dole, i ogólnie jest o wiele trudniej czytać i śledzić. Jest brzydka i nie podoba mi się.
Jestem pewien, że nie jestem pierwszym, który napotkał ten problem, więc moje pytanie brzmi: czy są jakieś dobrze ugruntowane wzorce najlepszych praktyk, które mogę zastosować, aby mój kod był czytelny pomimo korzystania z kodu asynchronicznego i wywołań zwrotnych?
LoadImageAsync
w rzeczywistości jest połączeniemExt.Ajax.request
z Sencha Touch.async.waterfall
to twoja odpowiedź.Odpowiedzi:
Jest bardzo mało prawdopodobne, że można osiągnąć za pomocą zwykłego js ten sam poziom zwięzłości i ekspresji w pracy z wywołaniami zwrotnymi, które ma C # 5. Kompilator wykonuje pracę, pisząc dla Ciebie całą tę płytkę, i dopóki środowiska wykonawcze js tego nie zrobią, nadal będziesz musiał od czasu do czasu przekazywać wywołanie zwrotne.
Jednak nie zawsze możesz sprowadzić wywołania zwrotne do poziomu prostoty kodu liniowego - rzucanie funkcjami nie musi być brzydkie, istnieje cały świat pracujący z tego rodzaju kodem, i zachowują rozsądek bez
async
iawait
.Na przykład użyj funkcji wyższego rzędu (mój plik js może być nieco zardzewiały):
źródło
Trochę mi zajęło odkodowanie, dlaczego robisz to w ten sposób, ale myślę, że może to być blisko tego, czego chcesz?
Najpierw uruchamia tyle żądań AJAX, ile może zrobić jednocześnie, i czeka, aż wszystkie zostaną zakończone, zanim włączy przycisk Start. Będzie to szybsze niż sekwencyjne oczekiwanie i, jak sądzę, jest o wiele łatwiejsze do naśladowania.
EDYCJA : Zauważ, że zakłada to, że masz
.each()
dostępne i żemyRepository
jest to tablica. Uważaj, jakiej iteracji pętli używasz zamiast tego, jeśli nie jest dostępna - ta wykorzystuje właściwości zamknięcia dla wywołania zwrotnego. Nie jestem jednak pewien, co masz dostępne, ponieważLoadImageAsync
wydaje się, że jest częścią specjalistycznej biblioteki - nie widzę żadnych wyników w Google.źródło
.each()
dostępne, a teraz, kiedy o tym wspominasz, ładowanie sekwencyjne nie jest absolutnie konieczne. Na pewno dam ci rozwiązanie. (Chociaż zaakceptuję odpowiedź Vskiego, ponieważ jest ona bliższa pierwotnemu, bardziej ogólnemu pytaniu).Oświadczenie: ta odpowiedź nie odpowiada konkretnie na twój problem, jest ogólną odpowiedzią na pytanie: „Czy istnieją pewne dobrze znane wzorce najlepszych praktyk, które mogę zastosować, aby zachować czytelność mojego kodu pomimo używania kodu asynchronicznego i wywołań zwrotnych?”
Z tego, co wiem, nie ma „dobrze ugruntowanego” wzorca, aby sobie z tym poradzić. Jednak widziałem dwa rodzaje metod stosowanych w celu uniknięcia zagnieżdżonych koszmarów wywołania zwrotnego.
1 / Używanie nazwanych funkcji zamiast anonimowych wywołań zwrotnych
W ten sposób unikniesz zagnieżdżenia, wysyłając logikę funkcji anonimowej w innej funkcji.
2 / Korzystanie z biblioteki zarządzania przepływem. Lubię używać Step , ale to tylko kwestia preferencji. Nawiasem mówiąc, to ten, którego LinkedIn używa.
Korzystam z biblioteki zarządzania przepływem, gdy używam wielu zagnieżdżonych wywołań zwrotnych, ponieważ jest ona dużo bardziej czytelna, gdy używa jej dużo kodu.
źródło
Mówiąc najprościej, JavaScript nie ma cukru syntaktycznego
await
.Ale przeniesienie części „końcowej” na dół funkcji jest łatwe; a dzięki natychmiastowemu wykonaniu funkcji anonimowej możemy uniknąć zadeklarowania odwołania do niej.
Możesz również przekazać część „end” jako wywołanie zwrotne sukcesu funkcji.
źródło