Ajax + zakres zmiennych w JS.

0

Witam!

Staram się rozkminić obsługę jquery.

jest sobie taki kod:

var resp=0;
function test()
{
 $.post("../ajax/email.php", { email: sender.value}, function(response){resp=response;});
 alert(resp);
}

I teraz niezależnie od tego co by skrypt email.php nie zwrócił, to pojawia się w alercie "0", tak jakby w ogóle nie przypisał tego w funkcji reagującej na odpowiedź. Jak to doprowadzic do ładu tak, żebym miał dostęp do odpowiedzi spoza metody $.post? Nie zależy mi zeby zmienna resp była globalna, moze być lokalna w funkcji test

0

Tu nie chodzi o zakres zmiennej, tylko asynchroniczność.

W JavaScripcie istnieje funkcyjny zakres widoczności zmiennej. Tj. zmienna zadeklarowana w funkcji f() będzie widoczna w całym jej obrębie (również w zagnieżdżonych funkcjach). I tyle jest w JavaScripcie do powiedzenia o zasięgu.

U Ciebie zmienna resp jest globalna (zadeklarowana "w funkcji globalnej", tj. poza jakąkolwiek funkcją) i jest widoczna wewnątrz test. Tyle że w momencie wykonania alert() wciąż ma wartość zero.

Szkopuł w tym, że funkcja $.post() jest asynchroniczna. Znaczy to, że jej wywołanie tylko inicjalizuje jakąś akcję, która jest wykonywana i kończona później, "w tle". Już po tym, jak sama funkcja $.post() się zakończy i sterowanie przejdzie do kolejnej funkcji, czyli u Ciebie alert().

Funkcja post() musi być asynchroniczna, bo śle na serwer żądanie ajaxowe (gdy "AJAX" był jeszcze skrótem, litera "A" oznaczała asynchroniczność). Można też powiedzieć, że funkcja post() jest "nieblokująca": rozpoczyna jakąś akcję (tj. śle żądanie ajaxowe), ale nie czeka na jej zakończenie, tylko zwraca sterowanie, cobyś mógł odpalić sobie jeszcze jakieś inne funkcje zanim akcja się zakończy, tj. zanim serwer zwróci odpowiedź.

To skąd masz u licha wiedzieć, kiedy asynchroniczna akcja się zakończy, czyli tutaj: kiedy przyjdzie odpowiedź z serwera? Ano dzięki ostatniemu parametrowi post(). Jest to funkcja. Takie funkcje nazwamy callbackami (po polsku: funkcjami wywołania zwrotnego :) :) ). Funkcja $.post() gwarantuje, że Twój callback zostanie wykonany po przyjściu odpowiedzi z serwera. I otrzyma za parametr treść tej odpowiedzi. Czyli: Twoja funkcja callbackowa, w której ustawiasz zmienną resp zostanie wywołana dopiero po przyjściu odpowiedzi ajaxowej.

Jeszcze raz, działa to tak:
0. Gdzieś globalnie ustawiasz zmienną resp na zero.

  1. Wywołujesz $.post(). Funkcja ta rozpoczyna procedurę wysyłania żądania ajaxowego na serwer, ale nie czeka na zakończenie tego żądania, tj. na przyjście odpowiedzi. Funkcja natychmiast się kończy, ale obiecuje, że w przyszłości, gdy z serwera przyjdzie odpowiedź, wywoła Twój callback, w którym ustawiasz zmienną resp na response.
    !!! Zauważ, że Twój callback w kroku #1 NIE został wywołany.
  2. Wywołujesz funkcję alert(), wyświetlając wartość zmiennej resp resp. Wartość zmiennej się nie zmieniła -- wciąż siedzi tam zero.
    (po paru sekundach, lub raczej iluś milisekundach)
  3. Z serwera przychodzi odpowiedź.
  4. Zgodnie z obietnicą, jaka dała funkcja $.post(), wywoływany jest Twój callback. Jako pierwszy argument, który nazwałeś sobie response, callback dostaje treść odpowiedzi. Ustawiasz zmienną resp na tę treść.
    !!! Zauważ, że nie wyświetlasz już nigdzie wartości zmiennej resp. Zrobiłeś to już dawno, gdy odpowiedź jeszcze nie przyszła i gdy w zmiennej resp siedziało zero.

Lek na Twoje problemy? Wyświetl wartość zmiennej resp wewnątrz callbacka, na jego końcu (wtedy równie dobrze możesz wyświetlić wartość zmiennej response). W ogóle, cokolwiek nie chcesz zrobić z odpowiedzią, musisz to zrobić wewnątrz callbacka. Oczywiście, możesz też gdzieś z boku zdefiniować sobie funkcję np. handleResponse(res), a wewnątrz callbacka umieścić wywołanie handleRespone(response).

Czemu to jest takie dzikie z tymi wywołaniami asynchronicznymi? Czemu to tak skomplikowane? Cóż... tak naprawdę to to jest bardzo proste. To normalne programowanie wielowątkowe jest turbo-szalenie skomplikowane i bardzo trudno nie popełnić w nim błędu. W JavaScripcie, dzięki tzw. pętli zdarzeń i jej konsekwencji -- wywołaniom asynchronicznym i callbackom -- NIE DA SIĘ doprowadzić do całej klasy błędów, z którymi normalnie trzeba się użerać w programowaniu wielowątkowym. Podpowiem, że Ty z poziomu kodu tego nie widzisz, ale żądanie ajaxowe może sobie iść w osobnym wątku. Dzięki asynchroniczności, Twój kod nie jest blokowany na czas trwania żądania, a jednocześnie nie musisz się martwić kolejnością wykonywania operacji wewnątrz jakiegoś bloku kodu / funkcji. W JavaScripcie wykonanie Twojego bloku kodu NIGDY nie zostanie przerwane przez jakieś inne bloki kodu.

0

$.post wykonuje się asynchronicznie, co oznacza, że przypisanie resp=response wykona się jakiś czas po zakończeniu funkcji test. zobacz sobie:

var resp=0;
function test()
{
 alert('1');
 $.post("../ajax/email.php", { email: sender.value}, function(response){alert('3');resp=response;});
 alert('2');
 alert(resp);
}

wg. mnie tego alerta powinieneś wywołać w callbacku

0

bswierczynski
biczuję się i wyznaczam sobie chłostę :/ W ogóle nie pomyslałem że to leci w osobnym wątku, baaa w ogóle nie pomyślałem ze JS jako język skryptowy może obsługiwać wątki :) ale to by się zgadzało. Problem polega na tym, ze w jednej funkcji potrzebuję wywołać parę razy AJAXa, i właśnie chciałem to sobie jeden po drugim załatwić a potem po wszystkich przeanalizować wszystkie odpowiedzi na raz. A tak to chyba będę musiał to robić kaskadowo.

krwq
No własnie na przykładzie wiem ze powinienem to wrzucic do ciała funkcji function(response){..} tylko ze powiedzmy, że funkcja test() wywołuje AJAXa 3 razy a potem w zależnosci od wszystkich 3 odpowiezi wykonuje pewne operacje.

0

możesz zawsze zrobić coś takiego:

function test(clb)
{
  // jakieś operacje
  $.post("../ajax/email.php", { email: sender.value}, function(response)
    {
      // jakies operacje, np.: response = JSON.parse(response);
      clb(response); // tutaj moze byc cokolwiek w parametrze
      // jakies operacje
    });
  // jakies operacje
}

// wywołanie np. tak:
test(function(r) {alert(r);}); 
0
misiakufal napisał(a):

W ogóle nie pomyslałem że to leci w osobnym wątku, baaa w ogóle nie pomyślałem ze JS jako język skryptowy może obsługiwać wątki :) ale to by się zgadzało.

Zdecydowana większość języków skryptowych posiada obsługę wątków, zauważalne wyjątki stanowią PHP (w końcu to tylko przekombinowany język szablonów) i JS właśnie. @bswierczynski nie wyraził się może dostatecznie jasno, JavaScript technicznie jest jednowątkowy, pracuje w oparciu o kolejkę zdarzeń, w danym momencie wykonuje się tylko jeden blok kodu, kiedy on się kończy to kolejny handler zostaje wyciągnięty z kolejki i odpalony. Całą asynchroniczność JS można streścić w krótkim "wykonaj potem".

0

hmm... no już czaję, cóż, trzeba będzie robić kaskadowo:/ dzieki za zainteresowanie:)

PS. Jednak postanowiłem wszystko zebrać do jednego skryptu i wywołać jeden skrypt z większą ilością parametrów po prostu:)

0

@misiakufal:
Chodzi o to, że z poziomu JavaScriptu wątków nie widzisz (odłóżmy na chwilę bajery w stylu WebWorkery) i nie musisz się nimi martwić. Jednak implementacja silnika JavaScript, np. silnik przeglądarki, MOŻE niektóre z Twoich operacji wykonywać wielowątkowo.

W przypadku NodeJS-a tak właśnie jest. Niektóre rzeczy, takie jak operacje dostępu do dysku czy zapytania do bazy danych, wykonują się w osobnych wątkach. Cały ciężar zarządzania wielowątkowością spoczywa jednak na silniku JavaScriptowym (tzw. środowisku wykonawczym). To programiści owego silnika muszą się napocić i martwić, żeby nic im się z tymi wątkami nie pokaszaniło. Ty tylko mówisz, że tu chcesz coś odczytać z dysku, tu coś posłać do bazy danych, a tam wysłać asynchroniczne żądanie. Środowisko wykonawcze niektóre lub wszystkie z tych rzeczy może sobie wykonać w osobnych wątkach -- tak, by było najwygodniej/najszybciej.

@krwq pisał, że nie jest powiedziane, iż żądanie ajaxowe faktycznie wykona się w osobnym wątku. To prawda -- to zależy od implementacji. I, co więcej, nie powinno mieć wpływu na nasz kod. My tylko wiemy, że funkcje ajaxowe działają asynchronicznie i nie wolno nam polegać na tym, że będzie inaczej. Sprawdziłam na szybko eksperymentalnie jak to jest w nowoczesnej przeglądarce (akurat w Fx) i wyszło mi, że faktycznie nasza funkcja wykonuje się dalej, a w międzyczasie -- równolegle -- wysyłane jest żądanie.

@PS:
Nie do końca rozumiem o co chodzi w pierwszym zdaniu Twojej wypowiedzi. Fakt, sam język JS nie definiuje obsługi wielowątkowości. Ba, specyfikacja ES5 nie wspomina nawet o pętli zdarzeń (!). Fakt faktem, że implementacje pętlę zdarzeń mają i że programiści JS przyzwyczajeni są do codziennego korzystania z wywołań asynchronicznych. I że to wszystko razem pozwala środowisku wykonawczemu na bardzo efektywne i fundamentalnie bezpieczne korzystanie z wielowątkowości. Z punktu widzenia programisty jest to całkowicie automatyczne.

Wg mnie to ciekawy sposób na wielowątkowość. NodeJS pokazuje, że w niektórych warunkach wydajność jest naprawdę spora -- również dlatego, że środowisko wykonawcze nie spawnuje nie wiadomo ilu wątków (w szczególności: ich liczba nie musi zależeć od liczby requestów) -- a programista wciąż nie musi zaprzątać sobie głowy standardowymi problemami charakterystycznymi dla programowania współbieżnego.

edit:
@misiakufal:
A co do kaskadowości -- nie wiem, czy musisz z niej tu korzystać. Jeśli nie zależy Ci na tym, by żądania wysyłać po kolei, a jedynie na tym, by przetworzyć odpowiedzi gdy już wszystkie przyjdą, to możesz puścić wszystkie N żądań równolegle. A po odbiorze każdej odpowiedzi możesz zapisać ją np. w tablicy oraz zinkrementować sobie jakiś licznik otrzymaneŻądania. Kiedy otrzymaneŻądania będzie równe N, możesz przetworzyć wszystkie odpowiedzi.

1 użytkowników online, w tym zalogowanych: 0, gości: 1