טיפול בשגיאות הוא אספקט יסודי של תכנות שמבטיח יישומים יכולים להתמודד בצורה אלגנטית עם מצבים בלתי צפויים. ב-JavaScript, בעוד ש-try-catch
משמש בדרך כלל, ישנן טכניקות מתקדמות יותר לשיפור טיפול בשגיאות.
מאמר זה בוחן את השיטות המתקדמות הללו, ומספק פתרונות מעשיים לשיפור אסטרטגיות ניהול השגיאות שלך ולהפוך את היישומים שלך לעמידים יותר.
מהו טיפול בשגיאות?
המטרה של טיפול בשגיאות
טיפול בשגיאות צופה, מזהה ומגיב לבעיות המתרחשות במהלך ביצוע התוכנית. טיפול נכון בשגיאות משפר את חוויית המשתמש, שומר על יציבות היישום ומבטיח אמינות.
סוגי שגיאות ב-JavaScript
- שגיאות תחביר. אלה הן טעויות בסינטקס הקוד, כמו חוסרים בסוגריים או שימוש לא נכון במילות מפתח.
- שגיאות ריצה. מתרחשות במהלך הביצוע, כגון גישה לנכסים של אובייקטים שאינם מוגדרים.
- שגיאות לוגיות. שגיאות אלו אינן גורמות לקריסת התוכנית אלא מובילות לתוצאות לא נכונות, לעיתים קרובות בגלל לוגיקה פגומה או תופעות לוואי לא מכוונות.
למה try-catch לא מספיק
המגבלות של try-catch
- מגבלות היקף. מטפל רק בקוד סינכרוני בתוך הבלוק שלו ואינו משפיע על פעולות אסינכרוניות אלא אם כן מטופלות במפורש.
- כישלונות שקטים. שימוש יתר או שימוש לא נכון יכולים להוביל לשגיאות שמתעלמים מהן בשקט, מה שעלול לגרום להתנהגות בלתי צפויה.
- הפצת שגיאות. לא תומך באופן טבעי בהפצת שגיאות דרך שכבות שונות של היישום.
מתי להשתמש ב-try-catch
- קוד סינכרוני. יעיל לטיפול בשגיאות בפעולות סינכרוניות כמו ניתוח JSON.
- קטעים קריטיים. השתמש כדי להגן על קטעי קוד קריטיים שבהם שגיאות יכולות להוביל לתוצאות חמורות.
מחלקות שגיאה מותאמות אישית: שיפור מידע על שגיאות
יצירת מחלקת שגיאה מותאמת אישית
מחלקות שגיאה מותאמות אישית מרחיבות את מחלקת Error
המובנית כדי לספק מידע נוסף:
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
this.stack = (new Error()).stack; // Capture the stack trace
}
}
יתרונות של שגיאות מותאמות אישית
- בהירות. מציעה הודעות שגיאה ספציפיות.
- טיפול גרנולרי. מאפשר טיפול בסוגי שגיאות ספציפיים בנפרד.
- מטא-דאטה על שגיאות. כולל הקשר נוסף על השגיאה.
מקרי שימוש לשגיאות מותאמות אישית
- כישלונות בדיקה. שגיאות הקשורות לאימות קלט המשתמש.
- שגיאות ספציפיות לדומיין. שגיאות המותאמות לדומיינים ספציפיים של יישומים כמו אימות או עיבוד תשלומים.
טיפול בשגיאות מרכזי
טיפול בשגיאות גלובלי ב-Node.js
מרכז טיפול בשגיאות באמצעות Middleware:
app.use((err, req, res, next) => {
console.error('Global error handler:', err);
res.status(500).json({ message: 'An error occurred' });
});
טיפול בשגיאות מרכזי ביישומי פרונטאנד
יישום ניהול שגיאות מרכזי ב-React באמצעות גבולות שגיאה:
class ErrorBoundary extends React.Component {
constructor(props) {
super(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 <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
יתרונות של ניהול שגיאות מרכזי
- עקביות. מבטיח גישה אחידה לניהול שגיאות.
- תחזוקה קלה יותר. עדכונים מרכזיים יפחיתו מהסיכון של איבוד שינויים.
- רישום וניטור טובים יותר. מקל על שילוב עם כלי ניטור.
העברת שגיאות
העברת שגיאות בקוד סינכרוני
השתמש ב-throw
כדי להעביר שגיאות:
function processData(data) {
try {
validateData(data);
saveData(data);
} catch (error) {
console.error('Failed to process data:', error);
throw error;
}
}
העברת שגיאות בקוד אסינכרוני
טפל בשגיאות עם promises או async
/await
:
async function fetchData(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Failed to fetch data:', error);
throw error;
}
}
מתי להעביר שגיאות
- שגיאות קריטיות. העבר שגיאות שמשפיעות על כל היישום.
- לוגיקת עסקים. ניתן להרשות לרמות גבוהות יותר ביישומים לטפל בשגיאות בלוגיקת עסקים.
טיפול בשגיאות בקוד אסינכרוני
טיפול בשגיאות עם async/await
השתמש ב-try-catch
כדי לנהל שגיאות בפונקציות async:
async function fetchUserData(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error('Failed to fetch user data');
}
return await response.json();
} catch (error) {
console.error('Error fetching user data:', error);
return null;
}
}
שימוש ב-Promise.all עם טיפול בשגיאות
טפל במספר פרומיסים ושגיאות:
async function fetchMultipleData(urls) {
try {
const responses = await Promise.all(urls.map
(url => fetch(url)));
return await Promise.all(responses.map(response => {
if (!response.ok) {
throw new Error(`Failed to fetch ${response.url}`);
}
return response.json();
}));
} catch (error) {
console.error('Error fetching multiple data:', error);
return [];
}
}
תחנות רכות נפוצות בטיפול בשגיאות אסינכרוני
- פרומיסים שלא נתפסים. תמיד טפל בפרומיסים באמצעות
await
,.then()
, או.catch()
. - כשלים שקטים. וודא ששגיאות לא מונחות באופן שקט.
- תנאי ראשון. נהיגה זהירה עם פעולות אסינכרוניות סמוכות.
רישומי שגיאות
רישומי שגיאות בלקוח
לכידת שגיאות גלובליות:
window.onerror = function(message, source, lineno, colno, error) {
console.error('Global error captured:', message, source, lineno, colno, error);
sendErrorToService({ message, source, lineno, colno, error });
};
רישומי שגיאות בשרת
שימוש בכלים כמו Winston לרישום בצד השרת:
const winston = require('winston');
const logger = winston.createLogger({
level: 'error',
format: winston.format.json(),
transports: [new winston.transports.File({ filename: 'error.log' })]
});
app.use((err, req, res, next) => {
logger.error(err.stack);
res.status(500).send('An error occurred');
});
ניטור והתראות
הגדרת ניטור והתראות בזמן אמת עם שירותים כמו PagerDuty או Slack:
function notifyError(error) {
// Send error details to monitoring service
}
שיטות מומלצות לרישום שגיאות
- כלול קשר. רישום קשר נוסף כמו נתוני בקשה ומידע על המשתמש.
- מנע רישום יתר. רישום מידע חיוני למניעת בעיות בביצועים.
- ניתוח רישומים באופן קבוע. בדיקת רישומים באופן קבוע כדי לזהות ולטפל בבעיות חוזרות.
התרדמת מדרג עדינה ותיקונים מצידיים
התרדמת מדרג עדינה
עיצוב היישום שלך כך שימשיך לפעול עם יכולות מוגבלות:
function renderProfilePicture(user) {
try {
if (!user.profilePicture) {
throw new Error('Profile picture not available');
}
return `<img data-fr-src="${user.profilePicture}" alt="Profile Picture">`;
} catch (error) {
console.error('Error rendering profile picture:', error.message);
return '<img src="/default-profile.png" alt="Default Profile Picture">';
}
}
מנגנוני תיקון מצידיים
ספק אלטרנטיבות כאשר הפעולות העיקריות נכשלות:
async function fetchDataWithFallback(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok');
}
return await response.json();
} catch (error) {
console.error('Error fetching data:', error);
return { message: 'Default data' }; // Fallback data
}
}
יישום התרדמת מדרג
- תיקוני ממשק משתמש. ספק אלמנטים חלופיים בממשק משתמש כאשר יש כשלונות בתכונות.
- תיקוני מידע. השתמש בערכים מאוחסנים או ברירת המחדל כאשר המידע החי אינו זמין.
- מנגנון ניסיון מחדש. הטמע לוגיקת ניסיון מחדש לשגיאות חומריות.
מאזן של התרדמת מדרג
איזון בין מתן גיבויים לבין שמירה על משתמשים מעודכנים לגבי בעיות:
function showErrorNotification(message) {
// Notify users about the issue
}
בדיקת טיפול בשגיאות
בדיקות יחידה לטיפול בשגיאות
אמת את טיפול השגיאות בפונקציות בודדות:
const { validateUserInput } = require('./validation');
test('throws error for invalid username', () => {
expect(() => {
validateUserInput({ username: 'ab' });
}).toThrow('Username must be at least 3 characters long.');
});
בדיקות אינטגרציה
בדוק טיפול בשגיאות במגוון שכבות של היישום:
test('fetches data with fallback on error', async () => {
fetch.mockReject(new Error('Network error'));
const data = await fetchDataWithFallback('https://api.example.com/data');
expect(data).toEqual({ message: 'Default data' });
});
בדיקות מקצה לקצה
דמיין תרחישים מהעולם האמיתי לבדוק טיפול בשגיאות:
describe('ErrorBoundary', () => {
it('displays error message on error', () => {
cy.mount(<ErrorBoundary><MyComponent /></ErrorBoundary>);
cy.get(MyComponent).then(component => {
component.simulateError(new Error('Test error'));
});
cy.contains('Something went wrong.').should('be.visible');
});
});
שיטות עבודה מומלצות לבדיקת טיפול בשגיאות
- כסה מקרים קצה. ודא שהבדיקות מתייחסות למגוון תרחישי שגיאה.
- בדוק גיבויים. אמת שמנגנוני הגיבוי פועלים כפי שהתכוונת.
- אוטומט את הבדיקות. השתמש בצינורות CI/CD כדי לאוטומט ולוודא טיפול חזק בשגיאות.
תרחישים מהעולם האמיתי
תרחיש 1: מערכת עיבוד תשלומים
טפל בשגיאות במהלך עיבוד התשלומים:
- מחלקות שגיאה מותאמות. השתמש במחלקות כמו
CardValidationError
,PaymentGatewayError
. - לוגיקה של ניסיון חוזר. יישם ניסיונות חוזרים לבעיות הקשורות לרשת.
- לוגינג מרכזי. נטר שגיאות תשלום וטפל בבעיות במהירות.
תרחיש 2: יישומים אינטנסיביים בנתונים
נהל שגיאות בעיבוד נתונים:
- הדרגתיות נאה. ספק נתונים חלקיים או תצוגות חלופיות.
- נתוני חילוף. השתמש בערכים מטמון או בברירת מחדל.
- רישום שגיאות. רישום הקשר מפורט לצורך פתרון בעיות.
תרחיש 3: אימות והרשאה של משתמש
טיפול בשגיאות אימות והרשאה:
- מחלקות שגיאה מותאמות. צור מחלקות כמו
AuthenticationError
,AuthorizationError
. - טיפול מרכזי. רישום ומעקב אחר בעיות הקשורות לאימות.
- נפילה עדינה. הצע אפשרויות כניסה אלטרנטיביות והודעות שגיאה משמעותיות.
מסקנה
טיפול מתקדם בשגיאות ב-JavaScript מחייב לצאת מעבר לשימוש פשוט ב- try-catch
כדי לקבל בברכה שגיאות מותאמות, טיפול מרכזי, העברה ובדיקות חזקות. החילול בטכניקות אלו מאפשר לך לבנות אפליקציות עמידות המספקות חווית משתמש חלקה, גם כאשר יש בעיות.
קריאה נוספת
- "JavaScript: The Good Parts" על ידי Douglas Crockford
- "You Don't Know JS: Async & Performance" על ידי Kyle Simpson
- מסמכי האינטרנט של MDN: טיפול בשגיאות
Source:
https://dzone.com/articles/advanced-error-handling-in-javascript-custom-error