Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 13 di 24
  • livello principiante
Indice lezioni

La richiesta fantasma

Gestire il problema di richieste non elaborate costruendo un meccanismo di time-out
Gestire il problema di richieste non elaborate costruendo un meccanismo di time-out
Link copiato negli appunti

Esistono diverse motivazioni per le quali una richiesta asincrona possa apparentemente svanire nel nulla. Tra le più frequenti troviamo i rallentamenti sulla rete e il sovraccarico del server, che potrebbe lasciare il navigatore in attesa di una risposta ma non essere in grado di elaborarla, se non entro tempi eccessivi.

L'utente in questi casi accusa un disservizio, che quando persistente, crea perplessità sulla professionalità dello sviluppatore del sito.

Quando il server non può rispondere ad una richiesta risponde con uno stato diverso da 200 (ok). Ma in caso di latenze questo non accade. È consigliabile quindi controllare il lasso di tempo intercorso tra l'azione richiesta da parte dell'utente e la reale possibilità di soddisfare il suo intento, specie qualora il server avesse frequenti problemi di banda o di velocità.

La reazione tipica dell'utente che non riceve risposta da una pagina è quella di tentare di ricaricarla ripetutamente. Questo comportamento sarebbe da evitare in quanto aggiunge richieste ad un server già saturo, che non riuscirà a smaltire il suo carico, continuando a lasciare l'utente in attesa di una risposta.

Le situazioni descritte possono essere affrontate in diversi modi, ne esaminiamo due tra più semplici.

Gestione di un time-out

Il meccanismo più importante che stiamo per mettere in piedi è una temporizzazione. Decidiamo di considerare la richiesta fallita dopo un certo tempo di attesa senza risposte ed in caso di fallimento avvisiamo l'utente.

La prima modifica di rilievo alla funzione utilizzata nella richiesta XML, ma riproponibile anche in quella testuale è il tipo di dato in ingresso. Non passiamo più la stringa contenente la url del documento da leggere ma il riferimento all'intero link. Modifichiamo quindi il comportamento sull'evento onclick scrivendo return caricaXML(this); al posto di return caricaXML(this.href);.

I commenti alla funzione caricaXML() sono molto utili a capire i passaggi e le rifiniture possibili di questo meccanismo. Facciamo caso in particolare ad un controllo sul link per evitare le richieste (click) ripetute, che può tornarci utile in molte situazioni.

Listato 21. La funzione caricaXML() con time-out

function caricaXML(link) {
  var  
    ajax = assegnaXMLHttpRequest(),
    elemento = prendiElementoDaId("contenuto-dinamico"),
    usaLink = true,

    // variabili di controllo del tempo
  
    // data di inizio interazione
    dataChiamata = new Date(),
    // tempo in millisecondi dell'inizio
    inizioChiamata = dataChiamata.getTime(),
    // secondi di attesa prima di fermare l'interazione
    massimaAttesa = 5,
    // variabile cui assegnare la funzione di verifica
    verificaTempoTrascorso;

  if(ajax) {
    usaLink = false;

    // parametro fittizio all'elemento link
    // per evitare un doppio click
    if(!link.clicked) {

      // se il parametro è risultato undefined o false
      // entriamo e gli assegnamo true
      link.clicked = true;

      // NOTA. Qualora l'elemento dovesse essere parte di un form si potrebbe
      // sfruttare il parametro "disabled", assegnandolo solo quando necessario.

      // effettuiamo la chiamata sul parametro href
      ajax.open("get", link.href, true);
      ajax.setRequestHeader("connection", "close");
      
      ajax.onreadystatechange = function() {

        // se la richiesta è già stata completata
        if(ajax.readyState === readyState.COMPLETATO) {
          // annulliamo la funzione di verifica tempo
          verificaTempoTrascorso = function(){};

          // e continuiamo con i normali controlli
          if(statusText[ajax.status] === "OK") {
            if(ajax.responseXML)
              elemento.innerHTML = parsaXml(ajax.responseXML);
            else
              elemento.innerHTML =
                "L'XML restituito dalla richiesta non è valido.<br />" +
                ajax.responseText.split('<').join("<").split('>').join(">");
          }
          else
            elemento.innerHTML =
              "Impossibile effettuare l'operazione richiesta.<br />" +
              "Errore riscontrato: " + statusText[ajax.status];

          // se il link non dovesse esser sovrascritto, come invece accade in questo esempio,
          // è importante ricordarsi di riabilitarlo reimpostando il parametro fittizio
          // Esempio: link.clicked = false;
        }

        // se la richiesta non è stata ancora ultimata
        // è possibile sfruttare la variabile massimaAttesa
        // per verificare se il controllo sul tempo trascorso
        // sia stato creato o meno.
        // Essendo tale valore un intero rappresentante i secondi
        // ma non essendo ancora stato riassegnato nel corrispettivo
        // in millesimi, questo else if può garantire che
        // il codice al suo interno verrà eseguito una sola volta
        else if(massimaAttesa < 1000) {           // conversione di massimaAttesain millisecondi
          massimaAttesa = massimaAttesa * 1000;

          // il controllo sul tempo trascorso deve essere
          // asincrono a questa funzione poichè non è detto
          // che il cambio di stato della richiesta
          // venga effettuato in tempi utili.
          // Una funzione apposita per la verifica
          // è la soluzione più indicata
          verificaTempoTrascorso = function() {

            // ogni chiamata asincrona a questa funzione
            // dovrà verificare la durata dell'interazione
            // è necessario quindi ridichiarare la variabile
            // al fine di ottenere il nuovo oggetto Date
            dataChiamata = new Date();

            // Se il tempo trascorso è maggiore della
            // massima attesa ...
            if((dataChiamata.getTime() - inizioChiamata) > massimaAttesa) {

              // ... interrompiamo la richiesta ed
              // informarmiamo l'utente di quanto avvenuto.

              // Quindi riassegnamo onreadystatechange ad una
              // funzione vuota, poichè quest'evento sarà
              // sollevato chiamando il metodo abort()
              ajax.onreadystatechange = function(){return;};

              // è possibile a questo punto richiamare il metodo abort
              // ed annullare le operazioni dell'oggetto XMLHttpRequest
              ajax.abort();

              // creiamo un elemento per avvertire l'utente
              // del fallimento della richiesta da aggiungere
              // a quello predisposto per mostrare il risultato.
              // Usiamo il metodo createElement() del document e
              // non innerHTML,che potrebbe riscrivere il link selezionato
              // annullando l'assegnazione del parametro fittizio.
              // Avendo annullato l'utilità dell'oggetto XMLHttpRequest
              // è possibile anche riciclare la variabile 'ajax'.
              ajax = document.createElement("p");
              ajax.innerHTML =
                 "Spiacente, richiesta fallita.<br />" +
                 "La prego di ritentare tra qualche istante.";

              elemento.appendChild(ajax);
       
       
              // è possibile ripristinare il parametro fittizio del link
              // su 'false' sia subito che con qualche istante di latenza,
              // Infatti l'attuale situazione può essere generata dalla
              // risposta lenta di un server sovraccarico e qualche attimo
              // di tranquillità in più non guasta.
              // Tuttavia per semplificare il codice ripristiniamo il link qui.
              link.clicked = false;
            }

            // se invece il tempo è inferiore al timeout
            else
              // si richiama questa stessa funzione, con un tempo
              // che non dovrà essere ne alto ne troppo basso.
              setTimeout(verificaTempoTrascorso, 100);
          };

          // definita la funzione non resta che avviarla
          verificaTempoTrascorso();
        };
      };
      ajax.send(null);
    };
  };
  return usaLink;
};

Quanto mostrato è utile soprattutto in situazioni critiche e potrebbe non esseresempre indispensabile ma il controllo sul link, che permette di filtrare le richieste, può essere la maggiore fonte di stabilità o velocità di una pagina potenzialmente ricca di accessi simultanei che con questo semplice accorgimento, non sovraccaricheranno il server con richieste multiple ed identiche.

Ti consigliamo anche