wtorek, czerwca 19, 2007

Magia CallBacka w JavaScript :-) - czyli jak przekazać dodatkowy parametr

Czasem w JavaScript'cie istnieje konieczność przekazania funkcji, która zostanie później wywołana przez cudzy kod (tzw. callback). Funkcja taka ma często przyjmować jakiś parametr, tudzież parametry które zostaną do niej przekazane z obcego kodu.
Sytuację taką mamy np. w trakcie oprogramowywania obsługi zdarzenia onSuccess obiektu Ajax.Request w bibliotece Prototype.
Np. tak jak to jest w poniższym przykładzie.


var handlerFunc = function(t) {
alert(t.responseText);
}

var errFunc = function(t) {
alert('Error ' + t.status + ' -- ' + t.statusText);
}

var handlerFunc = function(t) {
var xmlDoc = t.responseXML.documentElement;
//Handle data.
}

new Ajax.Request('/foo/bar', {parameters:'thisvar=true&thatvar=Howdy',
onSuccess:handlerFunc, onFailure:errFunc});


[powyższy przykład za http://wiki.script.aculo.us/scriptaculous/show/Ajax.Request]

Czasem jednak chcielibyśmy by do funkcji handlerFunc() przekazany został jeszcze jakiś nasz parametr i żeby to przekazanie wyglądało np. tak:

new Ajax.Request('/foo/bar', {parameters:'thisvar=true&thatvar=Howdy',
onSuccess:handlerFunc(index), onFailure:errFunc});


Okazuje się, że można to zrobić banalnie prosto ;-)
Przekazanie handlerFunc(index) jako callbacka spowoduje, że zostanie podjęta próba wywołania takiej funkcji:

handlerFunc(index)(data);

Wystarczy więc by funkcja handlerFunc(index) zwróciła odpowiednią funkcję przyjmującą argument data :-)

function handlerFunc(index) {
return function(data) {
var innerIndex=index;
alert(innerIndex+" "+data.responseText);
}
}

Co ciekawe, gdy użyjemy takiego kodu:

new Ajax.Request('/foo/bar', {parameters:'thisvar=true&thatvar=Howdy',
onSuccess:handlerFunc(1), onFailure:errFunc});
new Ajax.Request('/foo/bar2', {parameters:'thisvar=true&thatvar=Howdy',
onSuccess:handlerFunc(2), onFailure:errFunc});

To funkcja dla pierwszego requesta otrzyma parametr 1, a dla drugiego 2 :-)

Czyż JavaScript nie jest fajny? :-)

Podobne postybeta
Ajax - Prototype
overload vs varargs
Moja własna akcja w Google Home ;-)
Data binding w Polymerze jest oszukany ;-)
I jak "po bożemu" zrobić updatowanie widoku na podstawie danych z sieci w Androidzie?

1 komentarz:

  1. Tak, ładny trick.
    To pachnie rachunkiem funkcyjnym. W lambda rachunku (pewnie trochę "abuse of ntation" tu będzie, dawno się tym nie bawiłem), gdy mielibyśmy:

    handlerFunc = \lambda index data. f(index, data)

    moglibyśmy zdefiniować:

    function = handlerFunc(index)

    czyli function to funkcja polegająca na ustaleniu pierwszego argumentu w handlerFunc.

    W porządnym języku funkcyjnym (jak Lisp czy Haskell) dałoby się to zapisać w ten lub podobny sposób, w JavaScript jesteś niestety zmuszony definiować to jako funkcję zwracającą funkcję wprost.

    Ale to nie zmienia faktu, że to ładne i ociera sie niebanalną informatykę mimo ze problemik który rozwiązujesz jest bardzo "praktyczny".

    OdpowiedzUsuń