La gestione degli errori è un aspetto fondamentale della programmazione che garantisce che le applicazioni possano gestire con grazia situazioni impreviste. In JavaScript, mentre try-catch
è comunemente usato, ci sono tecniche più avanzate per migliorare la gestione degli errori.
Questo articolo esplora questi metodi avanzati, fornendo soluzioni pratiche per migliorare le tue strategie di gestione degli errori e rendere le tue applicazioni più resistenti.
Cosa sta gestendo gli errori?
Lo scopo della gestione degli errori
Gestione degli errori anticipa, rileva e risponde ai problemi che sorgono durante l’esecuzione del programma. Una corretta gestione degli errori migliora l’esperienza dell’utente, mantiene la stabilità dell’applicazione e garantisce affidabilità.
Tipi di errori in JavaScript
- Errori di sintassi. Questi sono errori nella sintassi del codice, come parentesi mancanti o utilizzo errato delle parole chiave.
- Errori di runtime. Si verificano durante l’esecuzione, come l’accesso alle proprietà di oggetti non definiti.
- Errori logici. Questi errori non causano la crash del programma ma portano a risultati errati, spesso a causa della logica imperfetta o degli effetti collaterali non intenzionali.
Perché il tentativo non è abbastanza
I limiti del cazzo di prova
- Limiti di portata. Solo gestisce il codice sincrono all’interno del suo blocco e non influisce sulle operazioni asincroni se non specificamente gestite.
- Fallimenti silenziosi. L’uso eccessivo o improprio può portare a errori essere silenziosamente ignorati, causando potenzialmente un comportamento inaspettato.
- Propagazione dell’errore. Non supporta nativamente gli errori di propagazione attraverso diversi livelli dell’applicazione.
Quando usare il try-catch
- Codice sincrono. Efficace per la gestione di errori in operazioni sincroni come l’analisi di JSON.
- Sezioni critiche. Utilizzare per proteggere le sezioni di codice critico in cui gli errori possono avere gravi conseguenze.
Classi di errore personalizzati: miglioramento delle informazioni sull’errore
Creazione di una classe di errore personalizzata
Le classi di errore personalizzate estendono l’integrazione Error
classe per fornire ulteriori informazioni:
class ValidationError extends Error {
constructor(message, area) {
tremendous(message);
this.identify="ValidationError";
this.area = area;
this.stack = (new Error()).stack; // Seize the stack hint
}
}
Vantaggi di errori personalizzati
- Chiarezza. Offre messaggi di errore specifici.
- Gestione granulare. Consente la gestione di tipi di errore specifici separatamente.
- Metadati di errore. Embrace un contesto aggiuntivo sull’errore.
Casi d’uso per errori personalizzati
- Guasti di convalida. Errori relativi alla convalida dell’enter dell’utente.
- Errori specifici del dominio. Errori su misura per specifici domini dell’applicazione come l’autenticazione o l’elaborazione dei pagamenti.
Gestione degli errori centralizzati
Gestione degli errori globali in node.js
Centralizzare la gestione degli errori utilizzando il middleware:
app.use((err, req, res, subsequent) => {
console.error('International error handler:', err);
res.standing(500).json({ message: 'An error occurred' });
});
Gestione degli errori centralizzati nelle applicazioni di frontend
Implementare la gestione degli errori centralizzati nella reazione usando i confini dell’errore:
class ErrorBoundary extends React.Part {
constructor(props) {
tremendous(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught by ErrorBoundary:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return ;
}
return this.props.kids;
}
}
Vantaggi della gestione degli errori centralizzati
- Coerenza. Garantisce un approccio uniforme alla gestione degli errori.
- Più manutenzione. Gli aggiornamenti centralizzati riducono il rischio di variazioni mancanti.
- Migliore registrazione e monitoraggio. Facilita l’integrazione con gli strumenti di monitoraggio.
Propagazione di errori
Propagazione dell’errore nel codice sincrono
Utilizzo throw
Per propagare gli errori:
operate processData(knowledge) {
attempt {
validateData(knowledge);
saveData(knowledge);
} catch (error) {
console.error('Didn't course of knowledge:', error);
throw error;
}
}
Propagazione dell’errore nel codice asincrono
Gestire errori con promesse o async
/await
:
async operate fetchData(url) {
attempt {
const response = await fetch(url);
if (!response.okay) {
throw new Error(`HTTP error! standing: ${response.standing}`);
}
return await response.json();
} catch (error) {
console.error('Didn't fetch knowledge:', error);
throw error;
}
}
Quando propagare gli errori
- Errori critici. Propagare gli errori che influiscono sull’intera applicazione.
- Logica aziendale. Consenti ai componenti di livello superiore di gestire errori logici aziendali.
Gestione degli errori nel codice asincrono
Gestione degli errori con asincroni/attesa
Utilizzo try-catch
Per gestire gli errori nelle funzioni asincroni:
async operate fetchUserData(userId) {
attempt {
const response = await fetch(`/api/customers/${userId}`);
if (!response.okay) {
throw new Error('Didn't fetch consumer knowledge');
}
return await response.json();
} catch (error) {
console.error('Error fetching consumer knowledge:', error);
return null;
}
}
Usando Promise.Ull con gestione degli errori
Gestire più promesse ed errori:
async operate fetchMultipleData(urls) {
attempt {
const responses = await Promise.all(urls.map
(url => fetch(url)));
return await Promise.all(responses.map(response => {
if (!response.okay) {
throw new Error(`Didn't fetch ${response.url}`);
}
return response.json();
}));
} catch (error) {
console.error('Error fetching a number of knowledge:', error);
return ();
}
}
Insidie comuni nella gestione degli errori asincroni
- Promesse non insegnate. Gestire sempre le promesse usando
await
,.then()
O.catch()
. - Fallimenti silenziosi. Assicurarsi che gli errori non siano inghiottiti silenziosamente.
- Condizioni di razza. Essere cauto con concorrente Operazioni asincroni.
Registrazione degli errori
Registrazione degli errori sul lato consumer
Cattura errori globali:
window.onerror = operate(message, supply, lineno, colno, error) {
console.error('International error captured:', message, supply, lineno, colno, error);
sendErrorToService({ message, supply, lineno, colno, error });
};
Registrazione degli errori lato server
Usa strumenti come Winston per la registrazione sul lato server:
const winston = require('winston');
const logger = winston.createLogger({
degree: 'error',
format: winston.format.json(),
transports: (new winston.transports.File({ filename: 'error.log' }))
});
app.use((err, req, res, subsequent) => {
logger.error(err.stack);
res.standing(500).ship('An error occurred');
});
Monitoraggio e avviso
Imposta monitoraggio in tempo reale e avvisi con servizi come Pagerduty o Slack:
operate notifyError(error) {
// Ship error particulars to monitoring service
}
Finest apply per la registrazione degli errori
- Includere il contesto. Registra un contesto aggiuntivo come i dati di richiesta e le informazioni dell’utente.
- Evita di sovralog. Registrare informazioni essenziali per prevenire i problemi di prestazioni.
- Analizza i registri regolarmente. Rivedere regolarmente i registri per rilevare e risolvere i problemi ricorrenti.
Degrado grazioso e fallback
Grazioso degrado
Progetta la tua applicazione per continuare a funzionare con capacità ridotte:
operate renderProfilePicture(consumer) {
attempt {
if (!consumer.profilePicture) {
throw new Error('Profile image not accessible');
}
return `
`;
} catch (error) {
console.error('Error rendering profile image:', error.message);
return '
'; }}
Meccanismi di fallback
Fornire different quando le operazioni primarie falliscono:
async operate fetchDataWithFallback(url) {
attempt {
const response = await fetch(url);
if (!response.okay) {
throw new Error('Community response was not okay');
}
return await response.json();
} catch (error) {
console.error('Error fetching knowledge:', error);
return { message: 'Default knowledge' }; // Fallback knowledge
}
}
Implementazione di un grazioso degrado
- Fallback UI. Fornire elementi dell’interfaccia utente alternativi quando le funzionalità falliscono.
- Fallback dei dati. Utilizzare valori memorizzati nella cache o predefiniti quando i dati Dwell non sono disponibili.
- Retry meccanismi. Implementare la logica di pensionamento per errori transitori.
Bilanciamento del degrado grazioso
Equilibrio che fornisce fallback con gli utenti informati sui problemi:
operate showErrorNotification(message) {
// Notify customers in regards to the situation
}
Check di gestione degli errori
Gestione degli errori di check unitaria
Verificare la gestione degli errori nelle singole funzioni:
const { validateUserInput } = require('./validation');
check('throws error for invalid username', () => {
anticipate(() => {
validateUserInput({ username: 'ab' });
}).toThrow('Username should be no less than 3 characters lengthy.');
});
Check di integrazione
Gestione degli errori di prova attraverso diversi livelli di applicazione:
check('fetches knowledge with fallback on error', async () => {
fetch.mockReject(new Error('Community error'));
const knowledge = await fetchDataWithFallback('https://api.instance.com/knowledge');
anticipate(knowledge).toEqual({ message: 'Default knowledge' });
});
Check end-to-end
Simula scenari del mondo reale per testare la gestione degli errori:
describe('ErrorBoundary', () => {
it('shows error message on error', () => {
cy.mount( );
cy.get(MyComponent).then(element => {
element.simulateError(new Error('Check error'));
});
cy.comprises('One thing went unsuitable.').ought to('be.seen');
});
});
Finest apply per la gestione degli errori di check
- CASSI DI COPERTURA. Assicurarsi che i check affrontino vari scenari di errore.
- Check di fallback. Verificare che i meccanismi di fallback funzionino come previsto.
- Automatizzare i check. Utilizzo Pipeline CI/CD Per automatizzare e garantire una robusta gestione degli errori.
Scenari del mondo reale
Situation 1: sistema di elaborazione dei pagamenti
Gestire errori durante l’elaborazione dei pagamenti:
- Classi di errore personalizzati. Usa le classi come
CardValidationError
,PaymentGatewayError
. - Retry Logic. Implementare i tentativi per problemi relativi alla rete.
- Registrazione centralizzata. Monitorare prontamente errori di pagamento e problemi di indirizzo.
Situation 2: applicazioni advert alta intensità di dati
Gestisci errori nell’elaborazione dei dati:
- Grazioso degrado. Fornire dati parziali o viste different.
- Dati di fallback. Utilizzare valori memorizzati nella cache o predefiniti.
- Registrazione degli errori. Registra un contesto dettagliato per la risoluzione dei problemi.
Situation 3: autenticazione e autorizzazione dell’utente
Gestire gli errori di autenticazione e autorizzazione:
- Classi di errore personalizzati. Crea classi come
AuthenticationError
,AuthorizationError
. - Gestione centralizzata. Registra e monitora i problemi relativi all’autenticazione.
- Grazioso degrado. Offri opzioni di accesso different e messaggi di errore significativi.
Conclusione
La gestione degli errori avanzati in JavaScript richiede di andare oltre il semplice try-catch
Abbracciare errori personalizzati, manipolazione centralizzata, propagazione e check robusti. L’implementazione di queste tecniche consente di creare applicazioni resilienti che offrano un’esperienza utente senza soluzione di continuità, anche quando le cose vanno male.
Ulteriori letture
- “JavaScript: The Good Elements” di Douglas Crockford
- “Non conosci JS: Async & Efficiency” di Kyle Simpson
- Documenti Net MDN: gestione degli errori