AJAX w jQuery i callbacki JavaScript
O tym, że jQuery jest niezłym narzędziem, nie trzeba nikogo przekonywać. Filozofia łańcuchowania – mam na myśli konstrukcję – nie jest jednak taka oczywista w użytku pod każdym względem. Z jQuery da się wygodnie pracować; to, że coś nie wychodzi wcale nie oznacza, iż trzeba od razu zmieniać używanego frameworka.
Wystarczy zrozumieć kilka rzeczy. Notka jest przeznaczona dla raczej początkujących; starzy wyjadacze pewnie niczego nowego się nie dowiedzą.
Sposób przekazywania zdarzeń
Zacznijmy od tego, jak skonstruowana jest większość funkcji, które pozwalają precyzować własne zdarzenia. Jest to niejako spowodowane konstrukcją samego języka. Przypomnijmy sobie dwie funkcje udostępniane przez przeglądarki oraz sposób podstawiania parametrów. Mam tu na myśli setInterval i setTimeout. Jeden z parametrów, to czas; drugi – zmienna przechowująca funkcję.
Tak, tak – jeśli do tej pory o tym nie wiedziałeś(aś) – w jQuery zmienna może być funkcją (również w innych językach możemy spotkać tego typu konstrukcje). Tak samo podpina się zdarzenia dla różnych obiektów, ale teraz wracam do tematu.
Callbacki w praktyce
Callback jest to – innymi słowy – funkcja uruchamiana przez jQuery/przeglądarkę, gdy jest wywoływane określone zdarzenie. I tutaj kluczowa uwaga – funkcja ta jest wywoływana asynchronicznie – czyli niezależnie od tych wykonywanych w dalszym ciągu skryptu.
Wróćmy jeszcze do przykładu z setTimeout:
setTimeout(function(){ alert('testujemy!') }, 5000);
alert('a wczesniej?');
Jak myślisz, co pokaże przeglądarka jako pierwsze? Testujemy, a zaraz po nim a wcześniej? Otóż nie – będzie dokładnie na odwrót, z tą różnicą, iż testujemy zostanie ukazane po 5 sekundach. Wynika to właśnie z powodu asynchroniczności. To tak, jakbyśmy programowali coś w stylu wielowątkowych skryptów.
Co z AJAX?
Wrzućmy teraz na warsztat jakieś proste żądanie AJAX napisane w jQuery:
$.get('skrypt.php', function(data){ alert('data'); });
Na pewno zauważyłeś analogię do powyższego przykładu. Tu również mamy do czynienia z callbackiem. Nie zawsze jest to komfortowa sytuacja, zwłaszcza w przypadku sprawdzania jakichś danych przez osobną funkcję, np:
function checkLogin(login){
$.get('skrypt', { user: login }, function(data){ return data; });
}
alert(checkLogin('ja'));
I co wówczas? Funkcja nie zwróci niczego. Teraz wyjaśnijmy, dlaczego tak się dzieje. Otóż – konstrukcja return użyta w callbacku zwraca dane – tak naprawdę – do frameworka, a nie do naszej funkcji. Jest to dość częsty błąd, który wymaga nieco potu. Zacznijmy od najprostszego rozwiązania…
Żądanie synchroniczne
Obiekt XmlHttpRequest, z którego korzysta każda biblioteka AJAX posiada własność umożliwiającą wysłanie żądania synchronicznego. Pozwala ono na uzyskanie mniej-więcej takiego efektu:
function checkLogin(login){
var result = '';
$.ajax({url: 'skrypt', data: { user: login }, success: function(data){ result = data; }, async: false});
return result;
}
Niestety, ma to dość poważną wadę – działanie skryptów jest zamrażane na czas żądania, co uniemożliwia działanie innych wątków naszej aplikacji.
Wykorzystanie callbacków
Pozostaje – w takim razie – wykorzystać w jakiś sposób wspomniane przeze mnie wyżej callbacki.
function checkLogin(login, callback){
$.get('skrypt', { user: login; }, function(data){ callback((data=='ok') });
});
}
Co spowoduje wywoływanie funkcji podanej jako callback z parametrem bool (prawda/fałsz). W naszym skrypcie można zrealizować to mniej więcej w ten sposób:
var login = $('#login').val();
checkLogin(login, function(isFree){
if(isFree){
alert('login jest wolny');
}else{
alert('login jest zajęty');
}
});
Wadą tego rozwiązania jest konieczność umieszczenia dalszej logiki funkcji w callbacku, co nie musi być koniecznie czytelne. Zaletą – możliwość zachowania wielowątkowości (np. Sprawdzanie wielu pól naraz).
Jak to wygląda?
No sam(a) widzisz. Sęk w tym, iż nie ma – mi znanego – jakiegoś innego sposobu, który umożliwiłby strukturalne przetworzenie takiego kodu. Wynika to głównie ze specyfiki oraz kontekstu wywoływania funkcji obsługi zdarzeń.
Polecam poczytanie na temat obiektowego podjescia do JS. To by bardzo ulatwilo. Podany kod mozesz bardzo ladnie uporzadkowac pamietajac o tym, ze jako callback dajesz funkcje. Mozesz ja przeciez przekazac przez referencje, np:
var mojaFunkcja = function(data){ …; }
$.get(’skrypt’, { user: login; }, mojaFunkcja);
Mozesz tez wykorzystac przemycic referencje z zewnatrz, by moc wykorzystac je podczas callbacka
var mojDiv = document.getElementById(„mojDiv”);
var mojaFunkcja = function(data){
mojDiv.innerHTML = data;
}
$.get(’skrypt’, { user: login; }, mojaFunkcja);
Aby zapewnic siebie, ze nic po drodze nie nadpisze Twojej referencji mozesz uzyc domkniecia (closure):
var mojaFunkcja = (
var mojDiv = document.getElementById(„mojDiv”);
return function(data){
mojDiv.innerHTML = data;
}
)();
$.get(’skrypt’, { user: login; }, mojaFunkcja);
Dzieki temu nic nie da rady nadpisac Twojego moj div, a nie musisz juz go poszukiwac podczas callbacka. Przydaje sie gdy masz miljard rekordow na stronie, a do kazdego z nich takie same akcje.
Najpiekniej jakbys np zamknal ajaxa w obiekcie, to juz bylo by niesamowicie czytelne
var model = function(id,login) {
this._div = document.getElementById(id);
var that = this;
this._login = login;
this.load = function() {
$.get(’skrypt’, { user: this._login; }, this._callback);
};
this._callback = function(data) {
that._div.innerHTML = data;
};
};
new model(„pole1″,”user1”);
Mogłem zrobic jakiegos typo, ale mam nadzieje ze sie przyda.