Errori
Parliamo degli errori.
Esistono due tipi di errori:
- errori previsti
- errori imprevisti
- errori off-by-one
Errori previsti
Gli errori previsti sono stati in cui accade la cosa sbagliata ma sai che potrebbe, quindi ti occupi di esso.
Sono cose come input dell'utente o richieste del server. Sai che l'utente potrebbe commettere un errore o che il server potrebbe essere inattivo, quindi scrivi un codice di controllo per assicurarti che il programma chieda di nuovo l'input, o che visualizzi un messaggio o qualunque altro comportamento sia appropriato.
Questi sono recuperabili quando gestiti. Se lasciati a mano, diventano errori imprevisti.
Errori imprevisti
Errori imprevisti (bug) sono stati in cui accade la cosa sbagliata perché il codice è sbagliato. Sai che alla fine succederanno, ma non c'è modo di sapere dove o come affrontarli perché, per definizione, sono inaspettati.
Sono cose come sintassi e errori logici. Potresti avere un errore di digitazione nel tuo codice, potresti avere chiamato una funzione con i parametri sbagliati. Questi non sono in genere recuperabili.
try..catch
Parliamo di try..catch
.
In JavaScript, throw
non è comunemente usato. Se ti guardi intorno con degli esempi di codice, saranno pochi e distanti tra loro e di solito sono strutturati sulla falsariga di
function example(param) {
if (!Array.isArray(param) {
throw new TypeError('"param" should be an array!');
}
...
}
Per questo motivo, anche i blocchi try..catch
non sono tutti così comuni per il flusso di controllo. Di solito è abbastanza semplice aggiungere alcuni controlli prima di chiamare i metodi per evitare errori attesi.
Anche gli ambienti JavaScript sono abbastanza indulgenti, quindi anche gli errori imprevisti vengono spesso ignorati.
try..catch
non deve essere raro. Ci sono alcuni casi d'uso, che sono più comuni in linguaggi come Java e C #. Java e C # hanno il vantaggio di aver digitato catch
costrutti, in modo da poter distinguere tra errori attesi e imprevisti:
C # :
try
{
var example = DoSomething();
}
catch (ExpectedException e)
{
DoSomethingElse(e);
}
Questo esempio consente ad altre eccezioni impreviste di scorrere e di essere gestite altrove (ad esempio registrandosi e chiudendo il programma).
In JavaScript, questo costrutto può essere replicato tramite:
try {
let example = doSomething();
} catch (e) {
if (e instanceOf ExpectedError) {
DoSomethingElse(e);
} else {
throw e;
}
}
Non elegante, che è parte del motivo per cui è raro.
Funzioni
Parliamo delle funzioni.
Se utilizzi il principio di responsabilità singola , ogni classe e funzione dovrebbe avere uno scopo singolare.
Ad esempio, authenticate()
potrebbe autenticare un utente.
Potrebbe essere scritto come:
const user = authenticate();
if (user == null) {
// keep doing stuff
} else {
// handle expected error
}
In alternativa potrebbe essere scritto come:
try {
const user = authenticate();
// keep doing stuff
} catch (e) {
if (e instanceOf AuthenticationError) {
// handle expected error
} else {
throw e;
}
}
Entrambi sono accettabili.
La promessa
Parliamo di promesse.
Le promesse sono una forma asincrona di try..catch
. Chiamando new Promise
o Promise.resolve
inizia il tuo codice try
. Chiamando throw
o Promise.reject
ti viene inviato il codice catch
.
Promise.resolve(value) // try
.then(doSomething) // try
.then(doSomethingElse) // try
.catch(handleError) // catch
Se hai una funzione asincrona per autenticare un utente, puoi scriverlo come:
authenticate()
.then((user) => {
if (user == null) {
// keep doing stuff
} else {
// handle expected error
}
});
In alternativa potrebbe essere scritto come:
authenticate()
.then((user) => {
// keep doing stuff
})
.catch((e) => {
if (e instanceOf AuthenticationError) {
// handle expected error
} else {
throw e;
}
});
Entrambi sono accettabili.
Nesting
Parliamo di nidificazione.
try..catch
può essere annidato. Il tuo metodo authenticate()
potrebbe avere internamente un blocco try..catch
come:
try {
const credentials = requestCredentialsFromUser();
const user = getUserFromServer(credentials);
} catch (e) {
if (e instanceOf CredentialsError) {
// handle failure to request credentials
} else if (e instanceOf ServerError) {
// handle failure to get data from server
} else {
throw e; // no idea what happened
}
}
Allo stesso modo le promesse possono essere annidate. Il tuo metodo async authenticate()
potrebbe utilizzare internamente promesse:
requestCredentialsFromUser()
.then(getUserFromServer)
.catch((e) => {
if (e instanceOf CredentialsError) {
// handle failure to request credentials
} else if (e instanceOf ServerError) {
// handle failure to get data from server
} else {
throw e; // no idea what happened
}
});
Quindi qual è la risposta?
Ok, penso che sia giunto il momento per me di rispondere effettivamente alla domanda:
Is a failure in authentication considered something you would reject a promise for?
La risposta più semplice che posso dare è che dovresti rifiutare una promessa ovunque vorrai altrimenti throw
un'eccezione se fosse un codice sincrono.
Se il tuo flusso di controllo è più semplice con alcuni controlli if
nelle tue dichiarazioni then
, non è necessario rifiutare una promessa.
Se il tuo flusso di controllo è più semplice rifiutando una promessa e controllando i tipi di errori nel codice di gestione degli errori, fallo invece.