JavaScript Gestione degli errori


Esempio

Gli errori generati dalle promesse vengono gestiti dal secondo parametro ( reject ) passato a then o dal gestore passato a catch :

throwErrorAsync()
  .then(null, error => { /* handle error here */ });
// or
throwErrorAsync()
  .catch(error => { /* handle error here */ });

chaining

Se si dispone di una catena di promesse, allora un errore causerà la mancata resolve gestori:

throwErrorAsync()
  .then(() => { /* never called */ })
  .catch(error => { /* handle error here */ });

Lo stesso vale per le tue funzioni then . Se un gestore di resolve genera un'eccezione, verrà richiamato il prossimo gestore di reject :

doSomethingAsync()
  .then(result => { throwErrorSync(); })
  .then(() => { /* never called */ })
  .catch(error => { /* handle error from throwErrorSync() */ });

Un gestore di errori restituisce una nuova promessa, consentendo di continuare una catena di promesse. La promessa restituita dal gestore degli errori viene risolta con il valore restituito dal gestore:

throwErrorAsync()
  .catch(error => { /* handle error here */; return result; })
  .then(result => { /* handle result here */ });

Puoi lasciare che un errore si sovrapponga a una catena di promesse rilanciando l'errore:

throwErrorAsync()
  .catch(error => {
      /* handle error from throwErrorAsync() */
      throw error;
  })
  .then(() => { /* will not be called if there's an error */ })
  .catch(error => { /* will get called with the same error */ });

È possibile generare un'eccezione che non è gestita dalla promessa avvolgendo l'istruzione throw all'interno di un callback setTimeout :

new Promise((resolve, reject) => {
  setTimeout(() => { throw new Error(); });
});

Funziona perché le promesse non possono gestire eccezioni generate in modo asincrono.

Rifiuti non gestiti

Un errore verrà ignorato silenziosamente se una promessa non ha un blocco catch o un gestore di reject :

throwErrorAsync()
  .then(() => { /* will not be called */ });
// error silently ignored

Per evitare ciò, usa sempre un blocco catch :

throwErrorAsync()
  .then(() => { /* will not be called */ })
  .catch(error => { /* handle error*/ });
// or
throwErrorAsync()
  .then(() => { /* will not be called */ }, error => { /* handle error*/ });

In alternativa, iscriviti all'evento unhandledrejection per raccogliere eventuali promesse respinte non gestite:

window.addEventListener('unhandledrejection', event => {});

Alcune promesse possono gestire il loro rifiuto più tardi del loro tempo di creazione. L'evento di rejectionhandled viene licenziato ogni volta che viene gestita una tale promessa:

window.addEventListener('unhandledrejection', event => console.log('unhandled'));
window.addEventListener('rejectionhandled', event => console.log('handled'));
var p = Promise.reject('test');

setTimeout(() => p.catch(console.log), 1000);

// Will print 'unhandled', and after one second 'test' and 'handled'

L'argomento event contiene informazioni sul rifiuto. event.reason è l'oggetto error e event.promise è l'oggetto promessa che ha causato l'evento.

In Nodejs le rejectionhandled e unhandledrejection eventi sono chiamati rejectionHandled e unhandledRejection sul process , rispettivamente, e hanno una firma diversa:

process.on('rejectionHandled', (reason, promise) => {});
process.on('unhandledRejection', (reason, promise) => {});

L'argomento reason è l'oggetto error e l'argomento promise è un riferimento all'oggetto promessa che ha causato l'attivazione dell'evento.

L'utilizzo di questi eventi unhandledrejection e rejectionhandled essere considerato solo a scopo di debug. In genere, tutte le promesse dovrebbero gestire i loro rifiuti.

Nota: al momento, solo Chrome 49+ e Node.js supportano eventi unhandledrejection gestiti e rejectionhandled .

Avvertenze

Concatenare con fulfill e reject

La funzione then(fulfill, reject) (con entrambi i parametri non null ) ha un comportamento unico e complesso e non dovrebbe essere utilizzata a meno che non si sappia esattamente come funziona.

La funzione funziona come previsto se data null per uno degli input:

// the following calls are equivalent
promise.then(fulfill, null) 
promise.then(fulfill)

// the following calls are also equivalent
promise.then(null, reject) 
promise.catch(reject)

Tuttavia, adotta un comportamento univoco quando vengono forniti entrambi gli input:

// the following calls are not equivalent!
promise.then(fulfill, reject)
promise.then(fulfill).catch(reject)

// the following calls are not equivalent!
promise.then(fulfill, reject)
promise.catch(reject).then(fulfill)

La funzione then(fulfill).catch(reject) then(fulfill, reject) sembra essere una scorciatoia per then(fulfill).catch(reject) , ma non lo è, e causerà problemi se usata in modo intercambiabile. Uno di questi problemi è che il gestore di reject non gestisce gli errori dal gestore di fulfill . Ecco cosa succederà:

Promise.resolve() // previous promise is fulfilled
    .then(() => { throw new Error(); }, // error in the fulfill handler
        error => { /* this is not called! */ });

Il codice sopra comporterà una promessa respinta perché l'errore è propagato. Confrontalo con il seguente codice, che si traduce in una promessa soddisfatta:

Promise.resolve() // previous promise is fulfilled
    .then(() => { throw new Error(); }) // error in the fulfill handler
    .catch(error => { /* handle error */ });

Un problema simile esiste quando si usa then(fulfill, reject) intercambiabile con catch(reject).then(fulfill) , eccetto con la propagazione di promesse soddisfatte invece di promesse respinte.

Lancio sincrono da una funzione che dovrebbe restituire una promessa

Immagina una funzione come questa:

function foo(arg) {
  if (arg === 'unexepectedValue') {
    throw new Error('UnexpectedValue')
  }

  return new Promise(resolve => 
    setTimeout(() => resolve(arg), 1000)
  )
}

Se tale funzione viene utilizzata nel mezzo di una catena di promesse, apparentemente non ci sono problemi:

makeSomethingAsync().
  .then(() => foo('unexpectedValue'))
  .catch(err => console.log(err)) // <-- Error: UnexpectedValue will be caught here

Tuttavia, se la stessa funzione viene chiamata al di fuori di una catena di promesse, allora l'errore non verrà gestito da esso e verrà lanciato all'applicazione:

foo('unexpectedValue') // <-- error will be thrown, so the application will crash
  .then(makeSomethingAsync) // <-- will not run
  .catch(err => console.log(err)) // <-- will not catch

Esistono 2 possibili soluzioni:

Restituisci una promessa respinta con l'errore

Invece di lanciare, fai come segue:

function foo(arg) {
  if (arg === 'unexepectedValue') {
    return Promise.reject(new Error('UnexpectedValue'))
  }

  return new Promise(resolve => 
    setTimeout(() => resolve(arg), 1000)
  )
}

Avvolgi la tua funzione in una catena di promesse

La tua frase di throw verrà presa correttamente quando si trova già all'interno di una catena di promesse:

function foo(arg) {
  return Promise.resolve()
    .then(() => {
      if (arg === 'unexepectedValue') {
        throw new Error('UnexpectedValue')
      }

      return new Promise(resolve => 
        setTimeout(() => resolve(arg), 1000)
      )
    })
}