Andrise programmeerimisalane WIKI

Ajax

Termin Ajax oli algselt akronüüm sõnadest Asynchronous JavaScript and XML ning see tähistab meetodeid serverist andmete laadimiseks, ilma et veebileht teeks selle käigus lehevahetusi. Akronüümina peaks sõna Ajax kõik tähed olema kirjutatud suurtähtedega (AJAX), aga kuna vahepeal on see varasem akronüüm muutunud omaette mõisteks ja kaotanud ka osa sisust (tähte X iseloomustav XML on Ajax päringutest praktiliselt kadunud), siis kasutatakse terminit mitte enam akronüümi vaid nimena.

NB! Siinne artikkel käsitleb Ajax päringute tegemist ILMA täiendavate teekideta. jQuery või Prototype vms. teegi meetodeid kasutades on Ajax päringute tegemine lihtsam, kuid alati ei ole 100 kB teegi kasutamine ainult ühe kindla tegevuse sooritamiseks põhjendatud ja seega oleks parem teada, kuidas sama asja ilma teegita teha.

XMLHttpRequest

Andmeid saab serverist jooksvalt laadida mitut moodi (näiteks kasutades peidetud freime), kuid siinkohal vaatame peamiselt kasutatavat XMLHttpRequest objekti. Tegu on kunagi veebipõhise Outlook'i jaoks välja mõeldud objektiga, mis võimaldab transportida (nagu nimest järeldada võib) XML dokumente üle HTTP protokolli. Õnneks oli see objekt disainitud nii, et XML'i kasutamine pole kohustuslik ning kui ka teised brauserid lisaks originaalsele Internet Explorerile selle objekti realiseerisid, saigi Ajax'i kasutamine võimalikuks.

Initsialiseerimine

Ajax päringu tegemiseks on vaja luua uus XMLHttpRequest objekti instants. Moodsates brauserites saab seda teha mugavalt samanimelise konstruktorfunktsiooniga, kuid vanemates Internet Exploreri funktsioonides tuleb kasutada ActiveX objekte.

Järgmine skript proovib tuvastada parimat võimalust XMLHttpRequest objektide loomiseks. Paremates brauserites on see kohe olemas, halvemates tuleb otsida ActiveX seest.

if (typeof XMLHttpRequest == "undefined"){
    XMLHttpRequest = function(){
        try {
            return new ActiveXObject("Msxml2.XMLHTTP.6.0");
        }catch(E1){};
        try{
            return new ActiveXObject("Msxml2.XMLHTTP.3.0");
        }catch(E2){};
        try{
            return new ActiveXObject("Msxml2.XMLHTTP");
        }catch(E3){};
        try{
            return new ActiveXObject("Microsoft.XMLHTTP");
        }catch(E4){};
        throw new Error("See brauser ei toeta XMLHttpRequest objekte.");
    }
}

Kui viga ei tekkinud, saab seejärel XMLHttpRequest objekti luua järgmiselt:

var request = new XMLHttpRequest();

Ühenduse avamine

Kui päringuobjekt on loodud, tuleb see edasisteks toiminguteks avada meetodiga open(meetod, url, async). Parameetriks meetod saab olla näiteks post või get; parameeter url tähistab veebiaadressi (NB! tuleb järgida same origin reeglit ehk et kasutada saab ainult samal domeenil asuvaid aadresse, kus asub eesolev veebileht!), mille sisu soovitakse laadida ja async väärtus peaks olema alati true, kuna vastasel korral pole tegu enam asünkroonse ühendusega ja termin Ajax kaotab sellega oma mõtte.

request.open("get", "/api/get_names", true);

Päringu päis

Päringuobjekti abil saab seada ka serverisse saadetava päringu päisesse suvalisi kirjeid. Näiteks üks populaarseimad võiks olla X-Requested-With: minu-super-agregaator. Samuti saab seada ka päringu mime tüüpi Content-type: application/json; Charset=UTF-8. Päisesse saab kirjeid lisada meetodiga request.setRequestHeader(name, value) kus name tähistab rea nimetust (Content-type) ja value väärtust (text/html). NB! Kuigi teoorias võib lisada sama nimega kirjet mitu korda ja lõplikus päringus kasutatakse neist viimase väärtust, ei pruugi see alati nii olla ja mõni brauser (IE) käitub teisiti, liites väärtused üheks pikaks stringiks kokku.

request.setRequestHeader("Content-type", "application/json; charset=utf-8");
request.setRequestHeader("X-Requested-With", "My-Awesome-Aggregator 1.0")

Sellisel juhul teeks brauser Ajax käsu serverile umbes sellisel kujul:

HTTP /api/get_names HTTP/1.1
Host: www.domeen.ee
Content-type: application/json; charset=utf-8
X-Requested-With: My-Awesome-Aggregator 1.0
(tühi rida)

Päist saab seega ära kasutada erineva meta-info seadmiseks (X- kirjed), mis päringu sisusse võibolla ei sobi või ei ole võimalik kasutada (näiteks kui sisu on binaarne), aga võib serveris asuvale skriptile siiski huvi pakkuda.

Päringu saatmine

Päringu saatmiseks serverile tuleb kasutada meetodit send(postBody). Parameeter postBody on vajalik POST tüüpi päringutes ning saab päringu sisuks. NB! Ka GET päringu puhul on antud parameetri sisestamine kohustuslik (vastasel korral annavad osad brauserid veateate) kuid selle väärtuseks võib olla näiteks tühi string, null vms.

request.open("post", "/api/set_name", true);
request.send("fname=Jaan%20Juurikas&lname=Tamm");

Serverile saadetav päring oleks sellisel juhul järgmine:

POST /api/set_name HTTP/1.1
Host: www.domeen.ee

fname=Jaan%20Juurikas&lname=Tamm

onreadystatechange

Kõige olulisemaks komponendiks Ajax'i päringu juures on onreadystatechange sündmuse haldaja, mis annab Ajax'ile õiguse kasutada nime alguses tähte A, kuna teeb päringu asünkroonseks. Iga kord kui brauser midagi päringuga teeb, käivitatakse just see sündmuse haldaja.

Sellest, kui kaugel brauser päringu sooritamisega parasjagu on, annab aimu omadus readyState. Antud omaduse väärtuseks on 1, kui open meetod on korrekselt käivitatud; 2, kui send päring on saadetud; 3, kui server saadab vastust brauserile ning 4, kui päring on lõpetatud. Peale staatuskoodi 4 saamist enam onreadystatechange funktsiooni ei käivitata. Päringu vastuse saab välja lugeda parameetrist responseText.

Lihtne sündmuse haldaja võiks olla näiteks järgmine:

request.onreadystatechange = function(){
    if(request.readyState == 4){
        alert("Serveri vastus: " + request.responseText);
    }
}

Oluline on ka omadus status - see näitab ära serveri poolt saadetud vastuse staatuskoodi. Juhul kui kood on 200, on kõik korras, kui aga midagi muud, siis mitte.

Juhul kui tegu on suuremahuliste andmetega või long polling päring, tasub tähelepanu pöörata ka staatuskoodile 3 - nimelt selle sündmuse juures on võimalik juba kasutada serveri poolt saadetud andmeid. Need ei ole küll veel lõplikud, aga siiski täiesti kasutatavad.

// ootame 10 kB
var total_length = 10*1024;
request.onreadystatechange = function(){
    if(request.readyState == 3){
        var status = Math.round(request.responseText.length/total_length*100)
        console.log("Staatus: " + status + " %");
    }
}
// Staatus: 40 %
// Staatus: 40 %
// Staatus: 80 %
// Staatus: 100 %

Internet Explorer võib tekitada mälulekke, kui onreadystatechange funktsioonis sisaldub viide mõne DOM elemendi juurde. Sellisel juhul jääb antud XMLHttpRequest objekti instants Garbage Collector'i poolt eemaldamata ja seda tuleb teha ise. Piisab lihtsalt uue tühja funktsiooni omistamisest onreadystatechange haldajale (st. peale seda, kui päring on lõpetatud ja request.readyState==4).

request.onreadystatechange = function(){};

Päringu katkestamine

Päringut saab vajadusel katkestada peale päringu algatamist meetodiga abort. Parimates brauserites katkestatakse sellega ühendus serveriga, halvemates (IE7+) ühendust küll ei katkestata, aga onreadystatechange sündmust enam välja ei kutsuta ning väga halbades (IE6) ei tehta üldse midagi.

Näide katkestab ühenduse 10 sekundi pärast peale ühenduse loomist.

request.send("");
window.setTimeout(function(){
    if(request.readyState<4 && request.abort)
        request.abort();
}, 10000)

Kokkuvõtteks

Ajax päringu sooritamine on ka ilma igasuguste teekideta suhteliselt lihtne, koosnedes paarist sammust.

  1. Loo uus XMLHttpRequest tüüpi objekt
  2. Ava objekt open meetodiga
  3. Sea päringu päised meetodiga setRequestHeader (valikuline tegevus)
  4. Saada päring teele meetodiga send
  5. Kuula sündmust onreadystatechange ning kui readyState väärtus on 4, loe vastus omadusest responseText
// kontrolli ''XMLHttpRequest'' toe olemasolu
if (typeof XMLHttpRequest == "undefined"){
    XMLHttpRequest = function(){
        try {
            return new ActiveXObject("Msxml2.XMLHTTP.6.0");
        }catch(E1){};
        try{
            return new ActiveXObject("Msxml2.XMLHTTP.3.0");
        }catch(E2){};
        try{
            return new ActiveXObject("Msxml2.XMLHTTP");
        }catch(E3){};
        try{
            return new ActiveXObject("Microsoft.XMLHTTP");
        }catch(E4){};
        throw new Error("This browser does not support XMLHttpRequest.");
    }
}
 
// Loo uus XMLHttpRequest objekt
request = new XMLHttpRequest();
 
// Ava objekt meetodiga .open
request.open("get","/api/get_name", true);
 
// Saada päring teele
request.send("");
 
// Kuula vastust
request.onreadystatechange = function(){
    if(request.readyState == 4){
    	if(request.status==200)
            alert("Serveri vastus: " + request.responseText);
        else
            alert("Veakood: "+ request.status);
    }
}