• בלוג
  • טיפ JavaScript - ניסוי עם ערך ברירת מחדל

טיפ JavaScript - ניסוי עם ערך ברירת מחדל

31/10/2023

בעבודה עם קוד אסינכרוני ב JavaScript טיפול בשגיאות הוא הכרחי, הבעיה שהמנגנון הקיים בשפה לטיפול בשגיאות בקוד אסינכרוני קצת מסורבל. נתבונן בקוד הבא:

async function sleepOrDie(ms, result) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() > 0.5) {
        resolve(result);
      } else {
        reject();
      }
    }, ms)
  })
}

async function handleClick() {
  const value = await sleepOrDie(50, 10);
  alert(value);
}

document.querySelector('button').addEventListener('click', handleClick);

אני יודע לא מתוחכם במיוחד אבל מספיק בשביל הדגמה. אם הפונקציה האסינכרונית מצליחה אז מקבלים ערך, אבל אם היא נכשלת נזרק Exception. זה עובד אם הטיפול שלנו בשגיאות הוא ב flow אחר ואז catch יוכל להפעיל את אותו flow. אבל מה אם אני פשוט רוצה לתת ל value ערך ברירת מחדל אם היתה שגיאה? למשל אם אני מנסה לקרוא ערך מבסיס נתונים או מהגדרות משתמש ואם הערך לא שם הכל בסדר והקוד ממשיך עם אותה ברירת מחדל. במצב כזה try/catch יכול להיראות מסורבל:

async function handleClick() {
  let value = 0;
  try {
    value = await sleepOrDie(50, 10);
  } catch (err) {

  }
  alert(value);
}

מבחינת נכונות הקוד טוב יותר, אבל מבחינת התחביר זה פשוט נראה רע: ה const הפך ל let לגמרי בלי סיבה, ואני תקוע עם בלוק catch ריק.

טכניקה טובה כדי להסתיר קוד מכוער היא לזרוק אותו לפונקציה. ריפקטורינג קצר ונקבל:

async function tryOr(fn, args, defaultValue) {
  try {
    return await fn.apply(null, args);
  } catch (err) {
    return defaultValue;
  } 
}

async function handleClick() {
  const value = await tryOr(sleepOrDie, [50, 10], 0);
  alert(value);
}

ומיטיבי לכת יכולים להוסיף את הפונקציה הזו לפרוטוטייפ של פונקציה ב JavaScript כדי שתהיה זמינה לנו בכל מקום בתוכנית:

Function.prototype.tryOr = async function (args, defaultValue) {
  try {
    return await this.apply(null, args);
  } catch (err) {
    return defaultValue;
  }
}


async function handleClick() {
  const value = await sleepOrDie.tryOr([50, 10], 0);
  alert(value);
}