Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Asynchroniczna pętla
Forum PHP.pl > Forum > Po stronie przeglądarki > JavaScript
Comandeer
Pytanie natury czysto teoretycznej. Załóżmy, że tworzę sobie API dla pewnego projektu X i musi być ono de facto w pełni asynchroniczne. Oczywistym wyborem są tutaj obiecanki - wszak nikt nie lubi się babrać w callbackach.

Wszystko jest cacy, ALE wink.gif Potrzebna jest mi asynchroniczna wersja pętli [].forEach - metoda X.each (żeby się nikt nie rąbnął z natywnym [].forEach; chyba żeby iść w coś typu X.forEachAsync?). Na każdym elemencie mogą być wykonywane operacje asynchroniczne i pętla powinna "czekać".

Na chłopski rozum implementacja powinna się opierać na Promise.all i każdy przepuszczany przez X.each element powinien być obiecanką. Tak na szybko "backend" czegoś takiego mógłby wyglądać np. tak (oczywiście pewnie będzie wyglądać ciut bardziej złożenie wink.gif):
Kod
var promises = [];

pool.forEach(function(item)
{
    var promise = toPromise(item);
    callback(promise);
    promises.push(promise);
});

return Promises.all(promises);



Zatem wywołanie X.each wyglądałoby następująco:
Kod
X.each(function(promise)
{
    
})
.then(function(returnValues)
{
    //po pętelce
});

User dostawałby po prostu dostęp do obiecanki - proste i przyjemne. Pojawia się jednak pewien problem: zaburza to koncepcję chainingu - przy takim wywołaniu nie dostaniemy pojedynczej wartości (a zatem mitycznego this) tylko tablicę dowolnych wartości (user może zwrócić wszystko w obiecance). W zależności od naszego widzimisię może to być dobrze lub źle (ale raczej źle). Stąd myślę czy nie ograniczyć możliwości usera wprowadzając pewien rodzaj nakładki, analogiczny do tego, jaką udostępniają np. taski asynchroniczne w Gruncie - user miałby dostęp tylko do funkcji done (z tym, że wówczas bebechy operowałyby raczej na pojedynczynm Promise a nie ich tablicy):
Kod
X.each(function(done)
{
    done(); //wszystko cacy
    done(jakakolwiekWartosc); //coś się zepsuło
})
.then(function(X)
{
    //itd
});

Tym sposobem w X.each().then na pewno uzyskamy z powrotem X, co np. w dalszej kolejności uprzyjemni obcowanie z biblioteką przy wykorzystywaniu generatorów.

Pytania brzmią:
  • Czy oparcie bebechów tego czegoś na Promise.all/Promise brzmi sensownie?
  • Czy lepiej udostępnić userowi dostęp do obiecanki wewnątrz X.each i nie mieć kontroli nad tym, co zwróci całość, czy dać mu jedynie dostęp do done i w pełni kontrolować to, co zostanie zwrócone?
  • X.each czy X.forEachAsync? wink.gif Chociaż pewnie każdy, kto pracował kiedyś z jQuery i tak wybierze 1. wersję
salfunglandyare
To zależy jak wierzysz w obiecanki smile.gif obiecanki-cacanki, a w życiu... no właśnie. Ja bym dał raczej dostęp na done, choć to nie zupełnie nie wygląda tak, jak to pokazałeś smile.gif
Comandeer
Cytat
Ja bym dał raczej dostęp na done, choć to nie zupełnie nie wygląda tak, jak to pokazałeś

Nie bardzo rozumiem - możesz rozwinąć?
salfunglandyare
.....done(function(){
});
Crozin
Czyżbyś szukał czegoś takiego? http://stackoverflow.com/a/23069551/252591
Comandeer
@salfunglandyare nie do końca o to chodzi. To, co pokazałeś, to pattern zaczerpnięty z jQuery, gdzie faktycznie obiekt Deferred posiada taką metodę. Mnie chodzi raczej o done jak tutaj: http://gruntjs.com/creating-tasks#custom-tasks (3 kod)

@Crozin ktoś tam coś zdrowo przekombinował… to, co pokazałeś, bardziej bym nazwał LazyPromise niż asynchroniczną pętlą (i w sumie podsunąłeś mi myśl jak taki LazyPromise zrobić wink.gif). W podanym przez Ciebie linku aż się prosi wykorzystać Promise.all - z tym, że nie wiem czy z jQuery by to zadziałało (bo oczywiście nie trzymają się standardu A+ tylko starszego…). Niemniej gościu na tyle przekombinował, że i tak mi się to przyda biggrin.gif
salfunglandyare
Rozumiem, ale done nie pojawi Ci się ot tak przy each czy foreach, jeśli robisz then w taki sposob, powinieneś również odpowiednio użyć done, inaczej wprowadzasz w blad. Niestety asynchroniczna synchroniczność na bazie promises w js to w dalszym ciagu callbacki na eventach w ladniejszym opakowaniu.
//edit: dodano "na eventach" powyzej
//edit2: poza tym wykonanie done w przypadku, który napisałeś to zupełnie co innego i jest troche oszukane tongue.gif done w tym przypadku jest f-cja wewnetrzna wywolywana coprawda asynchronicznie, ale nie majaca wplywu na kontekst
Comandeer
Dlatego wgl o tym done pomyślałem, żeby móc uruchamiać całość wewnątrz generatora, co by uprościło składnię do de facto minimum. Wówczas mielibyśmy prawie synchroniczny kod.

Owszem, done nie pojawi mi się od tak. Widzę to jako prostą funkcję, która resolvuje promise jeśli nic jej nie przekazaliśmy i rejectuje jeśli dostanie parametr. I funkcja ta byłaby przekazywana do pętli zamiast całej obiecanki. Co do wprowadzania w błąd - done nie jest częścią standardu obiecanek, więc nie bardzo wiem gdzie tu wprowadzanie w błąd? https://promisesaplus.com tą metodę wprowadza raptem kilka bibliotek + niekompatybilna ze standardem składnia jQuery. W natywnej implementacji obiecanek w ES6 metoda ta nie występuje, bo jedyne co robi to dubluje then
salfunglandyare
masz rację, ale to dlatego, że nie ma pomysłu do implementacji, co ona miałaby robić. Promises w założeniu są świetne, w wykonaniu muszą jeszcze poczekać. Done ma być ostatecznym krokiem, then powinien być jednostkowy. piszac .then().then().then().done() okreslsamy, ile ma być kroków do uzyskania. W tej chwili nie ma za dużej możliwości, żeby sprawdzić done, gdyż to jest kolejny element then, dlatego done = then... Osobiście wole polegac na $.ajax{ success: function(){moje_done() || moje_then()}} ale nie dlatego, że nie lubie obiecanek... po prostu w nie nie wierze tongue.gif
Comandeer
Z tym, że mówienie o obiecankach w kontekście jQuery to lekka herezja - tam to jest * a nie porządny promise wink.gif akurat ja uważam odwrotnie niż Ty i widziałbym po prostu then. No i to, co mówisz, to pasuje bardziej do finally, które kilka bibliotek zaimplementowało u siebie.
Poza tym done nie do końca zachowuje się jak then, bo faktycznie przerywa łańcuch wywołań. Niemniej nie wnosi de facto nic ponad to
Jeśli nie wierzysz w obiecanki, to polecam spojrzeć co na ich kanwie przygotowano w ES7, czyli async functions wink.gif
salfunglandyare
widzisz, ja widzialbym w obiecankach then = done = finally, ale nie jest tak prosto i nie bedzie. Ja sie nie upieram zupelnie na jquery ale widze, jak to dziala na innych bibliotekach i to sa po prostu eventy na zdarzeniach wywolujace callbacki jesli sa. Nie czarujmy sie, nie zrobisz z malucha mercedesa, chocby charczal i sapal jak rasowy merc. Dostajemy cos pomiedzy, co jest w sumie tak naprawde zupelnie niepotrzebne, bo bedzie wymienione na inne rozwiazania szybsze, bardziej odpowiadajace na takie problemy (proponuje porownanie promises, chocby tych z jquery z websockets)
Comandeer
Nie bardzo rozumiem co mają promises do modelu zdarzeniowego? Przecież one są całkowicie niezależne od siebie
by_ikar
Osobiście do promises/deferred po stronie serwera używam q: https://github.com/kriskowal/q#combination który posiada metodę all gdzie można wrzucić tablicę obietnic i każda z nich jest wykonywana asynchronicznie. Jakiś czas temu przerabiałem wiele libów do obietnic, i w sumie q, najbardziej przypadło mi do gustu, ze względu na możliwości i prostotę zapisu.

https://github.com/kriskowal/q/wiki/API-Ref...r-array-methods lepsza dokumentacja tej metody: https://github.com/kriskowal/q/wiki/API-Ref...r-array-methods
Comandeer
Q znam, ale raczej zostanę przy natywnej implementacji promises - w teorii powinno być szybciej (ale ręki se nie dam uciąć czy faktycznie jest, więc pewnie zostaną benchmarki)
by_ikar
Cóż, jeżeli chodzi o wydajność, to tutaj są jakieś świeże testy: https://github.com/petkaantonov/bluebird/tr...aster/benchmark i według nich, natywne implementacje nie koniecznie są takie szybkie jakby się mogło wydawać. Z tego wszystkiego wychodzi, że when.js jest na prawdę kawałkiem dobrego kodu, bo to już chyba któryś z rzędu benchmark w którym when.js stoi na wysokim poziomie. Czas zrewidować chyba swoją wiedzę i przyzwyczajenia o Q wink.gif
Comandeer
Cóz, bluebird od dawna jest jedną z najszybszych implementacji. Boli jedynie fakt, że implementacja natywna (a więc w c) jest 4 razy wolniejsza… na razie jednak zostanę przy natywnej, może nie dla samej wydajności, ale żeby nie mieć niepotrzebnego narzutu w kodzie. W razie potrzeby wplecenie w to bluebirda czy innej biblioteki będzie dziecinnie proste
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.