A completare la lista dei parametri utili ecco onreadystatechange
. Nella bozza di lavoro del W3C si parla di questo parametro come gestore di evento. In effetti il parametro serve ad assegnare una funzione che viene chiamata al variare del readyState
.
Quando in JavaScript si assegna un metodo si è soliti sfruttare il referente this
all'interno dello stesso al fine di essere certi che quanto richiesto faccia parte dello scope di tale oggetto. Chiariamo questo concetto con un esempio:
Listato 11. Assegnamento generico di un metodo ad un oggetto
var oggetto = new Object();
oggetto.testo = "html.it";
oggetto.funzione = function() {
alert(this.testo);
};
// una volta richiamato il metodo dell'oggetto ...
oggetto.funzione();
// la finestra di alert comunicherà
// html.it
// stringa presente all'interno del parametro oggetto.testo
Il comportamento appena descritto è più che normale, essendo funzione
un metodo dell'oggetto, ma cosa accadrebbe se si scegliesse di richiamare in modo asincrono lo stesso metodo?
Listato 12. Richiamiamo il metodo in modo asincrono
var oggetto = new Object();
oggetto.testo = "html.it";
oggetto.funzione = function() {
alert(this.testo);
};
setTimeout(oggetto.funzione, 50);
// risultato dopo 50 millisecondi una finestra con scritto
// undefined
Per risalire al campo testo
all'interno dello stesso oggetto è indispensabile utilizzare il nome dell'oggetto stesso come referente e non più this
.
Sostituiamo quindi alert(this.testo);
con alert(oggetto.testo);
. Questo è l'unico modo per raggiungere il parametro testo dell'oggetto utilizzato.
Il concetto vale allo stesso identico modo anche per la funzione assegnata ad onreadystatechange, contenendo a sua volta una funzione richiamata in modo asincrono. All'interno di questa funzione scriviamo i controlli in base ai parametri descritti ma il referente dovrà comunque essere il nome dell'oggetto e non this.
Listato 13. Esempio di funzione assegnata a onreadystatechange
ajax.onreadystatechange = function() {
if(ajax.readyState === 4) {
if(ajax.status == 200)
alert("Operazione effettuata con successo");
else
alert("Operazione fallita, errore numero " + ajax.status);
}
}
Ultimo minuzioso ed avanzato dettaglio prima di passare ad un esempio concreto, sconsigliabile a chi non ha troppa familiarità col JavaScript, è un piccolo approfondimento sul momento giusto in cui dichiarare onreadystatechange
.
Essendo richiamato in modo asincrono non è sbagliato assegnarlo dopo aver richiamato i metodi open
e send
. Il vantaggio è quello di evitare che la funzione assegnata, potenzialmente molto complessa, venga richiamata inutilmente più volte prima che readyState abbia un valore pari a 4. Anche senza questo accorgimento difficilmente abbiamo rallentamenti ma perchè non ottimizzare?
L'esempio pratico di un flusso dati asincrono è rappresentabile in questo modo:
Listato 14. Esempio di flusso asinrono
var oggetto = new Object();
oggetto.funzione = function() {
alert(oggetto.testo);
};
// imposto un timeout ancora prima di assegnare
// il parametro testo
setTimeout(oggetto.funzione, 0);
// finestra di avviso corretta!
oggetto.testo = "html.it";
Il valore del parametro testo, la stringa "html.it", viene assegnato solo dopo aver impostato il timeout sul metodo oggetto.funzione
con ritardo pari a zero millisecondi.
Potremmo pensare che la chiamata a tale metodo sia quindi immediata e che il parametro testo risulti "undefined" poichè definito dopo il setTimeout
.
In realtà non è così perchè gli intervalli di tempo, non sincronizzati con il normale flusso di esecuzione, vengono richiamati solo dopo che tutto il codice è stato processato per intero.
L'interprete infatti prima assegnerà al parametro testo il valore "html.it" e solo alla fine richimerà tutti gli eventi asincroni incontrati durante la lettura del codice, in questo caso il setTimeout
con latenza 0 per il metodo oggetto.funzione
.
L'unico modo per eliminare questa certezza è richiamare tra una chiamata asincrona, setTimeout
o setInterval
, una finestra di tipo "modal", come alert
o prompt
.
Inserendo una di queste dialog tra il setTimeout
e l'assegnazione del valore "html.it" al parametro testo, il flusso di esecuzione verrà interpretato solo fino a quel punto, gli intervalli asincroni assegnati fino a quel punto saranno richiamati e solo dopo, la finestra di avviso, verrà mostrata.
Ad esempio mettendo alert("prova");
tra il setTimeout
e la linea oggetto.testo = "html.it";
sarà possibile notare un'avviso con valore "undefined", ovvero l'esecuzione del setTimeout
sul metodo oggetto.funzione
e solo dopo l'alert con la scritta prova.
L'avviso con "undefined" confermerà quindi che il parametro testo non è stato ancora assegnato e che la funzione alert
ha modificato in qualche modo il flusso di esecuzione del codice, interrompendone l'interpretazione fino a quel punto.
Il consiglio è quindi di assegnare onreadystatechange
dopo il send
, ma evitando che tra il send e questa assegnazione vi sia codice per debug o altro che contenga un evento di tipo modal.
Per non avere problemi di sorta, il punto migliore in cui assegnare quest'ultimo parametro sarebbe tra open
e send
, come mostrato per comodità nei prossimi esempi. Farlo prima di richiamare open
risulterebbe possibile ma completamente inutile.