Məzmuna keç
Educora
İrəli22 dəq20 / 27

Xətaların emalı və sazlama

`try...catch`-dən o yana: daxili xəta tipləri, `cause` ilə öz xəta sinifləri, stek izini oxumaq, `debugger` və səhvləri tez tapmağa kömək edən konsol metodları.

Özünü yoxla
Bu dərsdə öyrənəcəksən
  • Daxili xəta tiplərini (TypeError, ReferenceError, RangeError, SyntaxError) və onların adi səbəblərini tanımaq
  • Öz xəta siniflərini yaratmaq, cause əlavə etmək və xətaları, o cümlədən asinxron kodda, tipinə görə emal etmək
  • Stek izini oxumaq və kəsmə nöqtələri, debugger və konsol metodları ilə sazlama aparmaq

İstifadəçi yazır: «Düymə heç nə etmir». Ekranda heç bir işarə yoxdur, amma konsolda qırmızı sətir var: Chrome-da TypeError: Cannot read properties of undefined (reading 'price') və altında fayl adları ilə rəqəmlər. Yeni başlayan üçün bu, səs-küydür, peşəkar üçün isə birbaşa səhvin yerini göstərən xəritə. Bu dərsdə həmin xəritəni oxumağı və öz xətalarını düzgün qurmağı öyrənəcəksən.

Daxili xəta tipləri

TipAdi səbəbNümunə
TypeErroryanlış tipli qiymətlə əməliyyat: funksiya olmayanı çağırmaq, undefined/null-un xassəsini oxumaqnull.name, (42)()
ReferenceErrormövcud olmayan (və ya TDZ-də olan) dəyişənconsole.log(missing)
RangeErroricazə verilən aralıqdan kənar ədədnew Array(-1)
SyntaxErroroxuna bilməyən kod və ya JSONJSON.parse('{bad}')
JavaScript
const attempts = [
  () => null.name,
  () => missingVariable,
  () => new Array(-1),
  () => JSON.parse('{bad json}'),
  () => (42)(),
];

for (const attempt of attempts) {
  try {
    attempt();
  } catch (error) {
    console.log(error.name, '|', error instanceof Error);
  }
}
▸ Gözlənilən nəticə
TypeError | true
ReferenceError | true
RangeError | true
SyntaxError | true
TypeError | true

Öz xətaların və cause

throw istənilən qiyməti ata bilər, amma həmişə Error obyekti at — stek izi yalnız onda olur. Yaxşı qayda: yalnız emal edə bildiyin xətanı tut, qalanını yenidən at (throw error). Proqramın müxtəlif xətalarını ayırmaq üçün Error-dan irs alan siniflər ierarxiyası qur — onda instanceof ilə hər növə uyğun reaksiya vermək olur.

JavaScript
class AppError extends Error {
  constructor(message, options) {
    super(message, options);
    this.name = this.constructor.name;
  }
}
class ValidationError extends AppError {}
class NotFoundError extends AppError {}

function findUser(users, id) {
  if (!Number.isInteger(id)) throw new ValidationError(`Bad id: ${id}`);
  const user = users.find((u) => u.id === id);
  if (!user) throw new NotFoundError(`User ${id} not found`);
  return user;
}

const users = [{ id: 1, name: 'Aysel' }];
for (const id of [1, 'x', 7]) {
  try {
    console.log(findUser(users, id).name);
  } catch (error) {
    if (error instanceof ValidationError) console.log('400', error.message);
    else if (error instanceof NotFoundError) console.log('404', error.message);
    else throw error;
  }
}
▸ Gözlənilən nəticə
Aysel
400 Bad id: x
404 User 7 not found
this.constructor.name hər alt sinfə avtomatik düzgün ad verir. 400 və 404 — HTTP status kodlarıdır, API dərsində onlara qayıdacağıq.

Aşağı səviyyəli xətanı tutub daha anlaşıqlı xəta atırsansa, orijinalı **cause** (ES2022) xassəsində saxla. Beləliklə, istifadəçiyə aydın mesaj göstərirsən, proqramçı isə əsl səbəbi itirmir.

JavaScript
function loadSettings(text) {
  try {
    return JSON.parse(text);
  } catch (error) {
    throw new Error('Settings file is broken', { cause: error });
  }
}

try {
  loadSettings('{ theme: dark }');
} catch (error) {
  console.log(error.message);
  console.log('Caused by:', error.cause.name);
  console.log(error.cause instanceof SyntaxError);
}
▸ Gözlənilən nəticə
Settings file is broken
Caused by: SyntaxError
true
JavaScript
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function saveScore(score) {
  await wait(100);
  if (score < 0) throw new RangeError('Score cannot be negative');
  return 'saved';
}

try {
  console.log(await saveScore(10));
  console.log(await saveScore(-5));
} catch (error) {
  console.log(`${error.name}: ${error.message}`);
}

const result = await saveScore(-1).catch((error) => `fallback (${error.message})`);
console.log(result);
▸ Gözlənilən nəticə
saved
RangeError: Score cannot be negative
fallback (Score cannot be negative)

Stek izini oxumaq

Stek izi (stack trace) xəta yarananda çağırışlar zəncirini göstərir, ən yenisi yuxarıda. Birinci sətir xətanın adı və mesajıdır, sonrakı sətirlər isə at funksiya (fayl:sətir:sütun) şəklindədir. Aşağıdakı cart.js faylında ikinci məhsulun product xassəsi yoxdur. Node.js-in çap etdiyi iz (qovluq yolları qısaldılıb, Node-un daxili sətirləri buraxılıb):

JavaScript
// cart.js
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.product.price * item.qty, 0);
}
function renderCart(cart) {
  return calculateTotal(cart.items);
}
renderCart({ items: [{ product: { price: 2 }, qty: 1 }, { qty: 3 }] });
Gözlənilən nəticə
TypeError: Cannot read properties of undefined (reading 'price')
    at cart.js:3:57
    at Array.reduce (<anonymous>)
    at calculateTotal (cart.js:3:16)
    at renderCart (cart.js:6:10)
    at Object.<anonymous> (cart.js:8:1)

Sazlama alətləri

  1. 1
    Səhvi təkrarla

    Səhvə gətirib çıxaran dəqiq addımları tap.

  2. 2
    Kəsmə nöqtəsi qoy

    DevTools › Sources panelində sətir nömrəsinə klik et və ya koda debugger; yaz — DevTools açıq olanda icra orada dayanacaq.

  3. 3
    Qiymətlərə bax

    Scope bölməsində dəyişənlərə bax, siçanı qiymətin üstünə gətir, Watch-a ifadə əlavə et.

  4. 4
    Addım-addım get

    Sətir-sətir irəlilə, funksiyanın içinə gir və ya ondan çıx.

  5. 5
    Düzəlt və yoxla

    Düzəlişdən sonra eyni addımları yenidən keç.

DevTools-u açF12
icranı davam etdirF8
növbəti sətrə keç (step over)F10
funksiyanın içinə gir (step into)F11
funksiyadan çıx (step out)Shift+F11

Konsolda log-dan başqa faydalı metodlar da var. Bu səhifədəki icraçı yalnız console.log-u göstərir, ona görə nümunəni brauzer konsolunda və ya Node.js-də işə sal. Aşağıdakı nəticə Node.js-dəndir; sort üçün ölçülən vaxt hər dəfə fərqli olur.

JavaScript
const students = [
  { name: 'Aysel', score: 92 },
  { name: 'Murad', score: 78 },
];

console.table(students);

console.group('Checkout');
console.log('items: 3');
console.warn('coupon expired');
console.groupEnd();

console.count('click');
console.count('click');
console.assert(students.length > 5, 'Expected more than 5 students');

console.time('sort');
students.sort((a, b) => b.score - a.score);
console.timeEnd('sort');
Gözlənilən nəticə
┌─────────┬─────────┬───────┐
│ (index) │ name    │ score │
├─────────┼─────────┼───────┤
│ 0       │ 'Aysel' │ 92    │
│ 1       │ 'Murad' │ 78    │
└─────────┴─────────┴───────┘
Checkout
  items: 3
  coupon expired
click: 1
click: 2
Assertion failed: Expected more than 5 students
sort: 0.027ms
Tapşırıq

ValidationError sinfini tamamla (name 'ValidationError' olsun) və parseAge(text) funksiyasını yaz. Mətn tam ədəd deyilsə, Not a whole number: <text>, 0–120 aralığından kənardırsa, Out of range: <age> mesajlı ValidationError at, əks halda yaşı qaytar.

Tapşırıq · JavaScript
class ValidationError extends Error {
  // set this.name in the constructor
}

function parseAge(text) {
  // Number(text) must be an integer from 0 to 120
}

for (const input of ['16', 'abc', '130', '42']) {
  try {
    console.log('Age:', parseAge(input));
  } catch (error) {
    if (error instanceof ValidationError) {
      console.log(`${error.name}: ${error.message}`);
    } else {
      throw error;
    }
  }
}
▸ Gözlənilən nəticə
Age: 16
ValidationError: Not a whole number: abc
ValidationError: Out of range: 130
Age: 42
Tapşırıq

retry(fn, times) funksiyasını yaz: fn-i uğur qazanana qədər ən çoxu times dəfə çağırsın və hər uğursuzluqda Retrying after: <mesaj> çap etsin. Bütün cəhdlər uğursuz olsa, sonuncu xətanı cause-da saxlayan All <times> attempts failed mesajlı xəta atsın.

Tapşırıq · JavaScript
function createFlakyTask(failuresBeforeSuccess) {
  let calls = 0;
  return () => {
    calls++;
    if (calls <= failuresBeforeSuccess) throw new Error(`Attempt ${calls} failed`);
    return `Success on attempt ${calls}`;
  };
}

function retry(fn, times) {
  // your code here
}

console.log(retry(createFlakyTask(2), 3));
try {
  retry(createFlakyTask(5), 3);
} catch (error) {
  console.log(error.message, '| cause:', error.cause.message);
}
▸ Gözlənilən nəticə
Retrying after: Attempt 1 failed
Retrying after: Attempt 2 failed
Success on attempt 3
Retrying after: Attempt 1 failed
Retrying after: Attempt 2 failed
Retrying after: Attempt 3 failed
All 3 attempts failed | cause: Attempt 3 failed

Əsas fikirlər

  • TypeError — yanlış tip və ya undefined, ReferenceError — naməlum dəyişən, RangeError — aralıqdan kənar qiymət, SyntaxError — oxunmayan kod və ya JSON.
  • Mesajın mətnini yox, error.name-i və ya instanceof-u yoxla — mesajlar brauzerə görə dəyişir.
  • Öz xəta sinifləri və cause xətaları mənalı edir və orijinal səbəbi saxlayır.
  • Asinxron xəta yalnız try içində await və ya .catch() ilə tutulur.
  • Stek izini öz faylının birinci sətrindən oxu; təxmin etmək əvəzinə kəsmə nöqtələri, debugger və konsol metodlarından istifadə et.

Özünü yoxla

10 sual. Hər düzgün cavab XP qazandırır.

1 / 10
const user = null; user.length; hansı xətanı verir?