Eccezioni nelle catene di Promise in JavaScript

Supponiamo che per qualche motivo la nostra Promise venga rifiutata con un errore:

javascriptlet promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    reject('error');
  }, 3000);
});

In questo caso, l'esecuzione salterà immediatamente al then che contiene un gestore di errore, o al primo catch incontrato, a seconda di quale venga trovato prima.

Ecco un esempio del primo caso:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  },
  function(error) {
    // l'esecuzione salterà immediatamente qui
  }
).then(
  function(result) {
    console.log(result);
  }
);

Ecco un esempio del secondo caso:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  }
).catch(
  function(error) {
    // l'esecuzione salterà immediatamente qui
  }
).then(
  function(result) {
    console.log(result);
  }
);

Il gestore di errore ha due possibilità: se riesce a gestire la situazione eccezionale, può restituire un risultato tramite return e l'esecuzione continuerà lungo la catena. Se invece non riesce a gestire l'errore, può o non restituire nulla, o lanciare un'eccezione tramite throw. In questo caso, l'esecuzione passerà al successivo gestore di errore (in un then o catch - a seconda di quale venga incontrato per primo).

Di solito, tutti gli errori della catena vengono gestiti in un unico punto: un catch viene posizionato alla fine della catena:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  }
).catch(
  function(error) {
    // arriveremo qui in caso di errore
  }
);

L'eccezione può originare dalla Promise stessa, oppure essere lanciata tramite throw in qualsiasi anello della catena:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    if (tuttoOk) {
      return result + '2';
    } else {
      throw new Error('errore'); // passiamo al gestore più vicino
    }
  }
)
.then(
  function(result) {
    return result + '3';
  }
).catch(
  function(error) {
    // gestore più vicino
  }
);

Tieni presente che catch serve proprio per diagnosticare l'errore: è risolvibile o meno. Se l'errore è risolvibile, il catch dovrebbe passarne la soluzione al then successivo. Se invece non è risolvibile (o questo specifico catch non sa come risolverlo), allora dovremmo o non restituire nulla o lanciare un'eccezione:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  }
).catch(
  function(error) {
    if (erroreRisolvibile) {
      return 'dati'; // inviamo al then successivo
    } else {
      // non restituiamo nulla o lanciamo un'eccezione
    }
  }
).then(
  function(result) {
    // qui risolviamo l'errore
  }
);