Please note, this is a STATIC archive of website developer.mozilla.org from 03 Nov 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Controllo del flusso di esecuzione e gestione degli errori

Questa traduzione è incompleta. Collabora alla traduzione di questo articolo dall’originale in lingua inglese.

JavaScript prevede un insieme di istruzioni compatto, specifico per il controllo del flusso di esecuzione, che si può utilizzare per aggiunge molta interettività nelle proprie applicazioni. Questo capitolo fornisce una panoramica su queste istruzioni.

Il JavaScript reference contiene dettagli esaustivi sulle istruzioni di questo capitolo. Il carattere punto e virgola (;) è usato per separare le varie istruzioni del codice JavaScript.

Ogni espressione JavaScript è a sua volta una istruzione. Si veda Espressione e operatori per una completa informazione sulle espresisoni.

Costrutto di blocco

Un costrutto fondamentale è il blocco, usato per raggruppare insieme più istruzioni, delimitato da un paio di parentesi graffe ({}):

{
  istruzione_1;
  istruzione_2;
  .
  .
  .
  istruzione_n;
}

Esempi

Il blocco è comunemente usato assieme alle istruzioni per il controllo del flusso (e.g. if, for, while).

while (x < 10) {
  x++;
}

Qui, { x++; } rappresenta un blocco.

Importante: JavaScript prima di ECMAScript2015 non aveva la visibilità di blocco. Le variabili definite nel blocco hanno una visibilità a livello di funzione o di script in cui sono contenute e l'effetto di assegnare loro un valore persiste oltre il blocco stesso. Cioè il blocco non definisce un campo di visibilità. I blocchi "Indipendenti" ("Standalone" blocks) in JavaScript possono dare un risultato differente da quello che avrebbero prodotto in C o in Java. Per esempio:

var x = 1;
{
  var x = 2;
}
console.log(x); // produce 2

Risulta 2 perché l'istruzione di definizione var x dentro il blocco ha lo stesso campo di visibilità dell'istruzione var x al di fuori del blocco. Mentre in C o Java, il codice equivalente avrebbe prodotto 1.

A partire da ECMAScript2015, la dichiarazione di variabile con l'istruzione let ha visibilità di blocco. Si veda il riferimento a let per ulteriori informazioni.

Costrutti Condizionali

Un costrutto condizionale è un insieme di istruzioni che vengono eseguite se una specifica condizione risulta vera. JavaScript prevede due costrutti condizionali: if...else e switch.

Costrutto if...else

Si usa il costrutto if se una condizione logica è vera. Si usa la clausola opzionale else per eseguire istruzioni in caso la stessa condizione sia falsa. Un costrutto if...else si presenta come qui sotto:

if (condizione) {
  istruzione_se_vera_la_condizione;
} else {// opzionale
  istruzione_se_falsa_la_condizione;
}

Qui la condizione è qualsiasi espressione che sia valutabile come vera oppure falsa (true o false). SI veda il riferimento a Boolean per una spiegazione su cosa possa essere valutabile come true o false. Se la condizione è valutata true, istruzione_se_vera_la_condzione verrà eseguita, altrimenti verrà eseguita istruzione_se_falsa_la_condzione. istruzione_se_vera_la_condzione verrà e istruzione_se_falsa_la_condzione possono essere una qualsiasi istruzione, incluso un altro if.

Si possono pure combinare le istruizioni else if per testare molteplici condizioni in sequenza, come il seguente codice dimostra:

if (condizione_1) {
  istruzione_1;
} else if (condizione_2) {
  istruzione_2;
} else if (condizione_n) {
  istruzione_n;
} else {
  ultima_istruzione;
} 

Nel caso di condizioni plurime solo la prima che sia valutata come vera sarà eseguita. Per eseguire più istruzioni si devono raggruppare in un blocco ({ ... }) . In generale è buona pratica usare il blocco specialmente se si usano if annidati:

if (condizione) {
  istruzione_1_eseguita_se_vera_la_condizione;
  istruzione_2_eseguita_se_vera_la_condizione;
} else {
  istruzione_3_eseguita_se_falsa_la_condizione;
  istruzione_4_eseguita_se_falsa_la_condizione;
}
Non è consigliabile usare la semplice assegnazione in una espressione condizionale, perché l'assegnamento potrebbe essere confuso con il segno di uguaglianza quando si dia un'occhiata rapida al codice. Ad esempio, non si usi il seguente codice:
 
if (x = y) {// questa è una assegnazione
  /* istruzioni qui */
}

Se si deve proprio usare un assegnamento in una espressione condizionale è pratica comune aggiungere un paio di parentesi tonde attorno all'assegnamento. Per esempio:

if ((x = y)) {
  /* istruzioni qui */
}

Valori valutabili a false

I seguenti valori si valutano a false (sono anche detti Falsy value):

  • false
  • undefined
  • null
  • 0
  • NaN
  • la stringa vuota ("")

Tutti gli altri valori, inclusi gli oggetti, saranno valutati a true in una istruzione condizionale.

Non si confonda il valori primitivi booleani true e false con i valori true e false dell'oggetto Boolean. Ad esempio:

var b = new Boolean(false);

if (b) // questa condizione è valutata a true
       // perché 'b' in questo caso è un oggetto
       // e si sa che tutti gli oggetti sono valutati a true

if (b == true) // mentre questa a false, 
               // perché si va a prelevare il contenuto dell'oggetto 'b' che è false

Esempi

Nell'esempio a seguire la funzione controllaDati ritorna true se il numero di caratteri contenuti nella proprietà value dell'oggetto Text (treCaratteri) è tre, altrimenti mostra un popup di alert e, infine, ritorna il valore false.

function controllaDati() {
  if (document.form1.treCaratteri.value.length == 3) {
    return true;
  } else {
    alert("Immetti esattemente tre caratteri. " +
    document.form1.treCaratteri.value + " non è valido.");
    return false;
  }
}

Costrutto switch

Un costrutto switch permette ad un programma di valutare un'espressione e tentare di trovare la corrispondenza esatta tra il valore risultante dalla valutazione dell'espressione e l'etichetta specificata nella clausola case. Se si incontra una corrisopondenza il programma eseguirà le istruzioni associate. Un costrutto switch si presenta  come nell'esempio qui sotto riportato:

switch (espressione) {
  case etichetta_1:
    istruzione_1
    [break;]// opzionale
  case etichetta_2:
    istruzione_2
    [break;]
    ...
  default:
    istruzioni_di_default
    [break;]
}

Il programma cerca la prima corrispondenza tra il valore ottentuto della valutazione dell'espressione e l'etichetta nella clausola case, poi trasferisce il controllo al corpo della medesima clausola eseguendo le istruzioni relative. Se non è stata trovata alcuna corrispondeza il programma va alla clausola opzionale default e, se la trova, esegue le istruizioni ad essa relative. Se non è stata data alcuna clausola default il programma continuerà con l'istruzione successiva al blocco switch. La clausola default è l'ultima che appare nel blocco switch, ma questa è una convenzione non la regola.

Il break opzionale, associato con ogni clausola case, assicura che il programma esca dal blocco switch una volta eseguite le istruzioni associate alla clausola e continui la sua esecuzione all'istruzione che segue il costrutto switch. Se il break è omesso il programma continua la sua esecuzione all'istruzione successiva nello stesso costrutto switch, ciò significa che eseguirà le istruzioni associate alla clausola case/default (se ci sono) successiva a quella appena terminata.

Esempi

Nel seguente esempio, se la variabile tipidifrutto contiene "Banane", il programma cercherà la corrispondente clausola case "Banane" ed eseguirà le istruzioni associate. Quando incontra il break, il porgramma esce dallo switch ed esegue l'istruzione successiva al blocco di switch. Se, invece, il break fosse stato omesso le istuzioni collegate con la clausola case "Ciliege" sarebbero state eseguite.

switch (titpidifrutta) {
  case "Arance":
    console.log("Le Arance sono a €0.59 il chilo.");
    break;
  case "Mele":
    console.log("Le mele sono a €0.32 il chilo.");
    break;
  case "Banane":
    console.log("Le Banane sono €0.48 il chilo.");
    break;
  case "Ciliege":
    console.log("Le ciliege sono s €3.00 il chilo.");
    break;
  case "Mango":
    console.log("Il Mango è è €0.56 il chilo.");
    break;
  case "Papaia":
    console.log("Il Mango e la Papaia sono a €2.79 il chilo.");
    break;
  default:
    console.log("Spiacenti, abbiano terminato " + fruittype + ".");
}
console.log("C'è qualcos'altro che ti piace?");

Costrutti di gestione delle Eccezioni

Si può sollevare/generare un'eccezione attraverso l'istruzione throw e si possono gestire usando il costrutto try...catch.

Tipi di eccezione

Quasi ogni tipo di oggetto può essere usato per sollevare/generare un'eccezione JavaScript. Tuttavia non tutti gli oggetti utili a questo scopo sono creati in modo eguale. Mentre è abbastanza comune usare numeri o stringhe per indicare un errore, è più efficace usare uno dei tipi di eccezione specificatamente creati a questo scopo:

Istruzione throw

Si usa l'istruzione throw per sollevare/generare un'eccezione. Quando si genera un'eccezione va specificata un'espressione che produca un valore da usarsi come eccezione:

throw espressione;

Si può usare una qualsiasi espressione con l'istruzione throw e non solamente espressioni di un certo tipo. Il seguente pezzo di codice lo dimostra:

throw "Errore2";  // tipo String
throw 42;         // tipo Number
throw true;       // tipo Boolean
throw {toString: function() { return "Sono un oggetto!"; } };
Note: Si può specificare un oggetto quando si genera un'eccezione. Si può poi far riferimento alle proprietà dell'oggetto nel blocco catch. Il seguente esempio crea un oggetto myUserException del tipo UserException e lo usa nell'istruzione throw.
// Crea un oggetto di tipo UserException
function UserException(messaggio) {
  this.message = messaggio;
  this.name    = "UserException";
}

// Sovrascrive il metodo toString() affinché l'oggetto
// dia una descrizione di se stesso al momento di usarlo come stringa. 
// (e.g. come messaggio nella console degli errori)
UserException.prototype.toString = function() {
  return this.name + ': "' + this.message + '"';
}

// Crea un'istanza dell'oggetto e lo usa nell'istruzione throw
throw new UserException("Valore troppo alto!);

Costrutto try...catch (... finally)

Il costrutto try...catch racchiude un blocco di istruzioni, che potrebbero generare un'eccezione, e specifica uno o più azioni per gestire l'eccezione che potrebbe essere sollevata. Se viene sollevata un'eccezione il costrutto try...catch la cattura.

Il costrutto try...catch è costituito da un blocco try, che contiene a sua volta uno o più istruzioni, e da al più (vedi nota più sotto) un blocco catch, contenenti istruzioni per gestire l'eccezione che eventulmente sarà sollevata all'interno del blocco try.  Cioè si vorrebbe che il blocco try fosse eseguito senza errori, ma se non fosse possibile si vuole che l'esecuzione passi al blocco catch. Se un'istruzione contenuta nel blocco (o in una funzione chiamata all'interno del blocco) try genera un'eccezione, il controllo passa immediatamente al blocco catch. Se nessuna eccezione è sollevata all'interno del blocco try, il blocco catch è semplicemente ignorato. Il blocco finally (opzionale se c'è il blocco catch, ma necessario se manca quest'utimo) è eseguito subito dopo l'esecuzione dei blocchi try/catch, ma prima di una qualsiasi istruzione che segua gli stessi try...catch...finally.

In realtà il browser Firefox è in grado di suppostare i blocchi catch condizionati, oltre quello incondizionato, rendendo virtualmeente illimitata la definizione di più di un blocco catch per uno stesso blocco try. Tuttavia questa caratteristica non è standard e se ne scoraggia l'uso, si veda a proposito la referenza try...catch.

L'esempio che segue usa il costrutto di try...catch. L'esempio chiama una funzione che ritorna il nome del mese estratto da un array grazie al parametro mo passato alla funzione. Se il valore passato non corrispondesse al numero di mese consentito (tra 1 e 12), un'eccezione verrebbe sollevata col valore "numeroMeseNonValido" e il blocco catch assegnerebbe alla variabile nomeDelMese il vaore di "sconoscuto".

function getNomeDelMese(mo) {
  mo = mo - 1; // Sistema il numero del mese (1 = Gen, 12 = Dic)
  var mesi = ["Gen","Feb","Mar","Apr","Mag","Giu","Lug",
                "Ago","Set","Ott","Nov","Dic"];
  if (mesi[mo]) {
    return mesi[mo];
  } else {
    throw "numeroMeseNonValido"; //l'istruzione throw è usata qui
  }
}

try { // blocco try
  nomeDelMese = getNomeDelMese(mese); // la funzione potrebbe sollevare un'eccezione
}
catch (eccezione) {
  nomeDelMese = "sconosciuto";
  logDegliErrori(eccezione); // Passa l'eccezione a un gestore -> una propria funzione

Il blocco catch

Si può usare il blocco catch per gestire le eccezioni che possono essere generate nel blocco try.

catch (catchID) {
  // istruzioni
}

Il blocco catch viene specificato un identificatore (catchID nel precedente esempio) che conterrà il valore specificato nell'istruzione throw. Si può usare questo identificatore per ottenere informazione ulteriori circa l'eccezione che è stata generata. JavaScript crea questo identificatore quando si entra nel blocco catch. L'identificatore è valido solo per la durata in esecuzione del blocco catch stesso, infatti usciti dal blocco catch termina la sua esistenza e non è più disponibile.

Per esempio, il seguente codice solleva un'eccezione. Quando l'eccezione si realizza il controllo passa al blocco catch.

try {
  throw "miaEccezione"; // genera una eccezione
}
catch (eccezione) { // "eccezione" è l'identificatore con conterrà
  // l'oggetto usato nell'istruzione thrown, in questo caso la stringa "miaEccezione" 
  
  // istruzioni che gestiscono l'eccezione
  gestisciErrori(eccezione); // passa l'oggetto eccezione al gestore
}

Il blocco finally

Il blocco finally contiene istruzioni che vengono eseguite subito dopo i blocchi try ed eventualmente catch, ma prima di ogni altra istruzione che segua il costrutto try...catch...finally. Il blocco finally è eseguito indipendentemente dal fatto che un'eccezione sia o meno generata. Se un'eccezione viene sollevata le istruzioni nel blocco finally saranno eseguite anche se il blocco catch corrispondente la gestisce.

Si può usare il blocco finally per permettere agli script di terminare elegantemente in presenza di un'eccezione, ad esempio, se si deve liberare una risorsa che lo script trattiene. Il seguente esempio apre un file e lo usa (JavaScript lato server permette di accedere al file system). Se si solleva un'eccezione mentre il file è aperto il blocco finally chiude il file prima che lo script termini/fallisca.

apriMioFile();
try {
  ScriviMioFile(dati); //Qui si può verificare un'eccezione/errore
} catch(e) {  
  gestisciErrore(e); // Se avviene un errore lo si gestisce
} finally {
  chiudiMioFile(); // chiude comunque la risorsa
}

Se il blocco finally ritorna un valore questo diviene il valore ritornato dall'intero costrutto try-catch-finally a dispetto di qualsiasi valore eventualmente ritornato dai blocchi try/catch:

function f() {
  try {
    console.log(0);
    throw "fasulla";
  } catch(e) {
    console.log(1);
    return true; // quasta istruzione di ritorno è sospesa
                 // finché il blocco finally non termina
    console.log(2); // istruzione non raggiungibile
  } finally {
    console.log(3);
    return false; // sovrascrive il precedente "return"
    console.log(4); // istruzione non raggiungibile
  }
  // "return false" è eseguito ora  
  console.log(5); // istruzione non raggiungibile
}
f(); // nel log a console troviamo stampato: 0, 1, 3 e false

La sovrascrittura dei valori di ritorno, da parte del blocco finally, colpisce anche le eccezioni generate e/o ri-generate dentro il blocco catch:

function f() {
  try {
    throw "fasulla";
  } catch(e) {
    console.log('catturata l\'eccezione interna "fasulla"');
    throw e; // Quasta istruzione throw è sospesa
             // finché il blocco finally non termina
  } finally {
    return false; // sovrascrive il precedente "throw"
  }
  // "return false" è eseguita ora
}

try {
  f();
} catch(e) {
  // Questo blocco non sarà mai raggiunto in quanto l'istruzione 
  // throw dentro il blocco catch (vedi più sopra) è sovrascritta
  // dal return della clausola finally
  console.log('catturata l\'eccezione esterna "fasulla"');
}

// OUTPUT
// catturata l'eccezione interna "fasulla"

try...catch innestati

Si possono annidare try...catch. Se un blocco try...catch interno non ha il blocco catch (in questo caso è d'obbligo che ci sia il blocco finally, anche vuoto, altrimenti si ha un errore sintattico), il blocco catch, del costrutto try...catch che lo racchiude, catturerà l'eventuale eccezione.

try{// try-catch esterno
  try{
   // try-finally interno
    throw "eccezione fasulla";
  }
  // Manca il blocco catch, ma deve esserci il blocco finally
  finally{
    // vuoto
  }
}
catch(e){
  // Viene catturata l'eccezione sollevata dal blocco più interno
  console.log("cattura esterna: " + e);
} 

//nella console sarà stampato: "cattura esterna: eccezione fasulla"

Utilizzo degli oggetti Error

A seconda del tipo di errore se si è in grado di usare le proprietà 'name' e 'message' si può avere un messaggio più ricco. 'name' fornisce la classe generale dell'Error (e.g., 'DOMException' o 'Error'), mentre 'message' generalmente fornisce un messaggio più conciso rispetto al convertire l'oggetto corrispondente all'errore in una stringa.

Se si crea una propria eccezione affiché ci si avvantaggi di queste proprietà (come nel caso, ad esempio, del blocco catch che non discrimini tra l'oggetto rappresentante la propria eccezione e quello di sistema) si può usare il costruttore dell'oggetto Error:

function faiQualcosaSoggettaAdErrore () {
  if (codiceCheProduceUnErrore()) {
    throw (new Error('Il Messaggio'));
  } else {
    faiQualcosaPerOttenereUnEorreDiJavascript();
  }
}
....
try {
  faiQualcosaSoggettaAdErrore();
} catch (e) {
  console.log(e.name);    // Scrive a console: 'Error'
  console.log(e.message); // Scrive a console: 'Il Messaggio' o un messaggio di errore di JavaScript)
}

I Promise

A partire da ECMAScript2015, JavaScript acquisisce il supporto agli oggetti Promise permettendo di controllare l'esecuzione di operazioni in modo differito e asincrono.

Un Promise può essere in uno di questi stati:

  • pending: stato iniziale, non fulfilled o rejected.
  • fulfilled: operazione risucita.
  • rejected: operazione fallita.
  • settled: il Promise è alternativamente fulfilledrejected, ma non pending.

Caricare un'immagine con XHR

Un esempio di semplice utilizzo di un Promise e XMLHttpRequest per caricare un'immagine è disponibile nel repository promise-test di MDN GitHub. Si può anche vedere in azione. Ogni passo  è commentato e ti permette di seguire il Promise e l'architettura XHR da vicino. Qui, invece, la versione non commentata che mostra l'utilizzo del Promise per darti un'idea del suo funzionamento:

function caricaImmagine(url) {
  return new Promise(function(resolve, reject) {
    var request = new XMLHttpRequest();
    request.open('GET', url);
    request.responseType = 'blob';
    request.onload = function() {
      if (request.status === 200) {
        resolve(request.response);
      } else {
        reject(Error('L\'immagine non è stata caricata con successo; codice di errore:' 
                     + request.statusText));
      }
    };
    request.onerror = function() {
      reject(Error('C'è stato un errore di connessione'));
    };
    request.send();
  });
}

Per informazioni più dettagliate si veda la pagina di riferimento relativa al Promise.

Tag del documento e collaboratori

 Hanno collaborato alla realizzazione di questa pagina: catBlack
 Ultima modifica di: catBlack,