- Introduzione ad AJAX: richieste sincrone ed asincrone -
 
COSA SERVE PER QUESTO TUTORIAL
Download | Chiedi sul FORUM | Glossario Conoscenze basiche di HTML e Javascript
Cos'è e come funziona AJAX

MITI DI AJAX
Favole e realtà sulla tecnica

AJAX è acronimo di Asynchronous Javascript And XML, una tecnica di programmazione ormai sempre più diffusa sul web. Prima cosa dunque è una tecnica e non un linguaggio: si basa su JS, il diffusissimo linguaggio di scripting lato client, e fa uso di alcune funzionalità per comunicare asincronamente (ovvero senza cambiare pagina) con il server attraverso, in maniera preferenziale ma non necessaria, XML. In sostanza usare AJAX significa programmare in Javascript utilizzando l'oggetto XMLHttpRequest.
Spesso si pensa poi che per utilizzare AJAX sia necessario un linguaggio lato server come PHP o ASP ma questo non è vero. È possibile ad esempio richiedere al server pagine statiche o comunque normali documenti XML che saranno poi elaborati lato client e quindi mostrati all'utente.

LE POTENZIALITÀ
L'obiettivo di AJAX

Uno dei principali svantaggi delle applicazioni web rispetto a quelle desktop è il fatto che nelle prime ogni input dell'utente per essere interpretato deve essere inviato al server, creando l'inconveniente di dover far scaricare una nuova pagina all'utente, con una notevole perdita di tempo. AJAX è nato per colmare questa differenza: è possibile infatti inviare e richiedere dati al server remoto attraverso Javascript, così eliminando la necessità di ricaricare la pagina.
Ad esempio AJAX permette di realizzare un sito web basato su una singola pagina: ammettiamo di avere un menu sulla parte sinistra della pagina e sulla destra il contenuto dell'articolo, grazie ad AJAX è possibile, quando l'utente clicca su un link del menu, scaricare il contenuto della pagina di destinazione in background (ovvero all'insaputa dell'utente) e porlo nella parte destra, quella del contenuto.
Prima di passare al codice facciamo però due importanti premesse. Prima cosa AJAX non funziona senza HTTP, il che vuol dire che è perfettamente normale se il codice stiamo per proporre produrrà errori su un percorso locale del tipo "C:\intepub\wwwroot\pagina.htm", è invece necessario utilizzare di un server, non importa se sia in remoto o in locale (un basilare scelta gratuita può essere lighttpd). Secondo il supporto di AJAX: gli esempi che seguono sono studiati per funzionare senza AJAX (e anche senza Javascript), ovviamente perdendo le sue potenzialità e funzionando come un normale sito; AJAX è supportato da Internet Explorer dalla versione 4, Opera dalla 8, Safari dalla 1.2, Mozilla dalla 1.0 (quindi anche da tutte le versioni di Firefox).

UN SITO SU UNA SOLA PAGINA CON AJAX
Un esempio di utilizzo di AJAX in maniera sincrona

Per prima cosa bisogna dire che l'oggetto XMLHttpRequest (come detto, il cuore di AJAX) può essere utilizzato in maniera sincrona o asincrona: nel primo caso l'esecuzione del codice viene interrotta, bloccando il browser temporaneamente fino al completamento della richiesta, mentre nel secondo, una volta eseguita la richiesta tutto funziona normalmente e al compimento dell'operazione viene richiamato l'evento onreadystatechange.
Vediamo prima il metodo sincrono. Creiamo una pagina HTML composta da due DIV, uno per il menu e uno per il contenuto.


<div class="menu">Menu<br></div>
<div id="content" class="content">DIV del contenuto</div>

All'interno del menu poniamo un certo numero di link simili a quelli che seguono, incrementando l'indice del contenuto (che ci servirà poi per sapere quale file richiedere, come pagina1.txt, pagina2.txt ecc., che naturalmente devono esistere e contenere qualche testo):


<a href="pagina1.htm" onclick="return CaricaContenuto(1);">Pagina 1</a><br>
<a href="pagina2.htm" onclick="return CaricaContenuto(2);">Pagina 2</a><br>
<a href="pagina3.htm" onclick="return CaricaContenuto(3);">Pagina 3</a><br>
[...]

Al click su uno di essi viene eseguita la funzione CaricaContenuto che inoltra la richiesta dei dati al server e scrive la risposta all'interno del DIV del contenuto. Si noti il fatto che nell'evento onClick viene restituito il valore a sua volta restituito da CaricaContenuto: se dall'evento onClick si restituisce false il link viene ignorato e dunque non vi è reindirizzamento, se invece viene restituito true, l'anchor si comporta normalmente. CaricaContenuto è predisposto in modo tale da restituire true quando rileva che AJAX (ovvero l'oggetto XMLHttpRequest) non è supportato dal browser. Questa tecnica permette ai browser senza supporto per Javascript o per XMLHttpRequest di essere reindirizzati ad una pagina statica, se così non fosse, per essi sarebbe impossibile visitare il sito.
Analizziamo ora la funzione CaricaContenuto:


// Funzione richiamata quando si clicca su un link del menu, richiede al
// server e mostra i dati in questione
function CaricaContenuto(id) {
    // Otteniamo l'oggetto XMLHttpRequest
    var request = GetXMLHttpRequest()
    // Nel caso AJAX non sia supportato restituisce true, il che permette
    // di essere reindirizzati al file specificato nell'href, infatti il
    // valore restituito dall'evento onClick indica se ignorare href o
    // meno
    if (request == null) return true;
    // Predispone una richiesta di tipo GET a pagina#.txt sincrona (ultimo 
    // parametro false), ovvero arresta l'esecuzione fino al 
    // completamento dell'operazione
    request.open("GET", "pagina" + id + ".txt", false);
    // Esegue l'operazione senza inviare alcun dato specifico
    request.send(null);
    
    // Se l'operazione ha avuto successo (codice HTTP 200) sostituisce
    // il contenuto del DIV "content" con il testo scaricato altrimenti
    // mostra codice e testo dell'errore
    if(request.status == 200) {
        document.getElementById("content").innerHTML = request.responseText;
    } else {
        alert("Si è verificato un errore: \n" + request.status + " - " + request.statusText)
    }
    return false;
}

Sostanzialmente essa tenta di creare XMLHttpRequest con la funzione GetXMLHttpRequest, che vedremo in dettaglio tra poco. Se quest'ultima ha come risultato null, AJAX non è supportato, dunque CaricaContenuto restituisce true (quindi, come appena detto, il link si comporterà normalmente, reindirizzando ad un file HTML statico); in caso contrario predispone la richiesta XMLHttpRequest con il metodo open e la inoltra con send. Come si può vedere a open vengono comunicati il tipo di richiesta, l'URL desiderato, e un valore booleano che indica se la richiesta deve essere asincrona (true) o meno (false). I tipi di richiesta sono tipicamente tre (ma in questo articolo ci occuperemo solamente della prima):

  • GET: normale richiesta HTTP, la query (i dati) è inviata come parte dell'URL (es. http://dominio/path?ParametroA=ValoreA&ParametroB=ValoreB) ed ha una dimensione limitata a circa 2 Kb;
  • POST: normale richiesta HTTP, la query (i dati) è inviata separatamente dall'URL, offre una maggiore sicurezza dei dati e non ha limitazioni di dimensione;
  • HEAD: richiesta del tutto uguale a GET ma richiede solamente le intestazioni (codice HTTP, data di modifica e altri dati simili) e non il contenuto;

Essendo la richiesta sincrona, il codice si blocca su send fino al completamento dell'operazione. A questo punto viene verificato il risultato attraverso la proprietà status che rappresenta il codice HTTP della risposta del server: ad esempio 200 indica che l'operazione è riuscita, 404 che il file richiesto non esiste e così via. Se non vi sono errori (codice HTTP 200, appunto), il contenuto del DIV con ID content viene rimpiazzato con il responso della richiesta, ottenuto tramite la proprietà responseText. In caso di errori si notifica testo e codice di risposta all'utente. Infine, dato che AJAX è supportato, viene restituito false.
Vediamo ora un importante passaggio che abbiamo tralasciato: la creazione dell'oggetto XMLHttpRequest:


// Funzione che ottiene (nelle sue varie forme a seconda del browser)
// l'oggetto XMLHttpRequest
function GetXMLHttpRequest() {
    var obj;
    // Dichiariamo una variabile che indica se l'oggetto è stato creato
    // o meno dato che si procederà per tentativi
    var creato = false;
    try {
        // Crea l'oggetto XMLHttpRequest (funziona con Firefox, Mozilla, 
        // Opera, Safari e Internet Explorer dalla 7)
        obj = new XMLHttpRequest();
        creato = true;
    } catch (e) {
        // Per le versioni precedenti alla 7 di IE si procede tentando di
        // creare l'oggetto ActiveX XMLHttpRequest, che assume nomi diversi
        // a seconda delle versioni
        var IEXMLHttpRequestNames = new Array("Microsoft.XmlHttp", "MSXML4.XmlHttp", "MSXML3.XmlHttp",
            "MSXML2.XmlHttp", "MSXML.XmlHttp");
        
        for (var i = 0; i < IEXMLHttpRequestNames .length; i++) {
            try {
                obj = new ActiveXObject(IEXMLHttpRequestNames[i]);
                creato = true;
            } catch (e) { }
        }
    }
    
    // Se è stato possibile creare l'oggetto lo restituisce altrimenti
    // da come risultato null
    if (creato) {
        return obj;
    } else {
        return null;    
    }
}

La funzione GetXMLHttpRequest, tenta di creare XMLHttpRequest utilizzando dapprima un semplice new XMLHttpRequest (per IE dalla versione 7, Mozilla, Firefox, Opera e Safari), poi tenta di creare l'ActiveX (per le versioni di IE precedenti alla 7) con nomi diversi finché non vi riesce. Se nessuno di questi metodi ha successo, AJAX non è supportato, dunque viene restituito null.
Il risultato ottenuto sarà dunque una pagina HTML dotata di menu con alcuni link, cliccando sui quali il contenuto del DIV a fianco cambierà dinamicamente, senza dover ricaricare la pagina.

RICHIESTE ASINCRONE
La via asincrona: nessun inconveniente per l'utente

In precedenza abbiamo detto che vi è anche la possibilità di effettuare la richiesta al server in maniera asincrona, dunque senza bloccare l'esecuzione, metodo leggermente più complesso ma indispensabile in caso di scarsa connettività o di necessità di dover scaricare grosse quantità di dati. Per eseguire la richiesta asincronamente dobbiamo servirci dell'evento onreadystatechange che viene chiamato ogni volta che cambia lo stato di avanzamento della richiesta, identificato dalla proprietà readyState. readyState che può assumere i seguenti valori: richiesta non iniziata (0), invio richiesta (1), richiesta inviata (2), alcuni dati ricevuti (3), richiesta completata (4). Mettiamo dunque un riferimento ad una nuova funzione chiamata request_readystatechange nella function di creazione dell'oggetto XMLHttpRequest; prima di return obj in GetXMLHttpRequest aggiungiamo quanto segue:


// Impostiamo request_readystatechange come gestore dell'evento 
// onreadystatechange
obj.onreadystatechange = request_readystatechange;

Ed ecco di seguito la funzione request_readystatechange:


// Funzione richiamata quando l'operazione di richiesta AJAX cambia il suo stato:
// non iniziata (0), invio richiesta (1), richiesta inviata (2), alcuni dati 
// ricevuti (3), richiesta completata (4). In questo caso ci interesseremo solo
// all'ultima evenienza
function request_readystatechange() {
    // Quando è stata completata l'operazione (readyState 4)
    if (request.readyState == 4) {
        // Se l'operazione ha avuto successo (codice HTTP 200) sostituisce
        // il contenuto del DIV "content" con il testo scaricato altrimenti
        // mostra codice e testo dell'errore
        if(request.status == 200) {
            document.getElementById("content").innerHTML = request.responseText;
        } else {
            alert("Si è verificato un errore: \n" + request.status + " - " + request.statusText)
        }
    }
}

Questa funzione verrà richiamata in varie occasioni con readyState diversi, ma noi siamo in questo caso interessati solo a essere notificati del completamento dell'operazione (request.readyState == 4); il codice all'interno del blocco if che verifica il readyState è uguale in tutto e per tutto a quello che seguiva il metodo send nella funzione CaricaContenuto, da cui va eliminato. CaricaContenuto risulterà quindi così:


// Funzione richiamata quando si clicca su un link del menu, richiede al
// server e mostra i dati in questione
function CaricaContenuto(id) {
    // Otteniamo l'oggetto XMLHttpRequest
    request = GetXMLHttpRequest()
    // Nel caso AJAX non sia supportato restituisce true, il che permette
    // di essere reindirizzati al file specificato nell'href, infatti il
    // valore restituito dall'evento onClick indica se ignorare href o
    // meno
    if (request == null) return true;
    // Predispone una richiesta di tipo GET a pagina#.txt asincrona (ultimo 
    // parametro true), ovvero non arresta l'esecuzione: la conclusione
    // dell'operazione verrà segnalata a request_readystatechange
    request.open("GET", "pagina" + id + ".txt", true);
    // Esegue l'operazione senza inviare alcun dato specifico
    request.send(null);

    return false;
}

Una volta richiamato send l'esecuzione del codice continua e la richiesta viene portata a termine in background. Si noti che il terzo parametro della chiamata a open è questa volta true: ciò indica che la richiesta deve essere asincrona. Inoltre è necessario spostare la dichiarazione di request (l'oggetto XMLHttpRequest) a livello globale, cosicché sia accessibile anche da request_readystatechange. Porre quindi la dichiarazione di request in cima allo script:


<script type="text/javascript" language="javascript">

// L'oggetto request è globale perché sia accessibile anche da 
// request_readystatechange
var request;

CONCLUSIONI
Cosa abbiamo ottenuto

In questo articolo abbiamo visto come AJAX può rivoluzionare la programmazione web grazie all'eliminazione della tradizionale necessità di ricaricare le pagine. Le applicazioni di questa tecnica possono essere molte altre, dalla validazioni di campi in tempo reale ad una chat, dalla notifica dei movimenti dell'utente al classico completamento automatico. È stata poi data una panoramica dei due metodi, sincroni o asincroni, e delle loro differenze: si ricordi a questo proposito che è in genere preferibile utilizzare il metodo asincrono così da non arrestare l'esecuzione dell'intero browser.
 

<< INDIETRO by VeNoM00