مدیریت خطا با استفاده از async / await و promiseها

گردآوری و تالیف : عرفان کاکایی
تاریخ انتشار : 15 مرداد 1397
دسته بندی ها : جاوا اسکریپت

من عاشق promiseها هستم. Promiseها یک مدل شگفت‌انگیز برای رفتار ناهمگام هستند، و await نیز جلوگیری از callback را بسیار آسان‌تر می‌کند. پس از این که توانستید یک مدل ذهنی از نحوه کار promiseها بسازید، می‌توانید در چندین خط کد، جریان‌های ناهمگام بسیار پیچیده‌ای بسازید.

با این که داشتن async / await در جعبه‌ابزار خود را بسیار دوست دارم، تعداد زیادی تناقص در مدیریت خطاها هنگام استفاده از آن‌ها وجود دارد. نوشتن مدیریت خطا به گونه‌ای که خطاهای بیشتری از آنچه که شما می‌خواهید را در بر بگیرد، بسیار آسان است و استراتژی مورد استفاده، برخی برتری‌ها در خوانایی که async / await فراهم می‌کنند را خنثی می‌کند.

با async / await، یکی از راه‌های رایج برای مدیریت خطاها وقتی که منتظر یک promise هستید، این است که آن را در یک بلوک try / catch جمع کنید. این کار، به یک مورد شکست ساده بر می‌خورد: اگر هر کار دیگری داخل بلوک try خود انجام دهید، یک خطای exception دریافت می‌شود.

async / await معمولی

async () => {

  try {

    const data = await fetchData();

    doSomethingComplex(data);

  } catch (e) {

     }

}

این یک اثر متقابل ناخوش‌آیند میان async / await و exceptionهای JS است. اگر JS مکانیزمی داشت که فقط expceptionهایی خاص را دریافت کند، می‌توانستیم خطاهایی که می‌خواهیم مدیریت کنیم را با دقت بیشتری توضیح دهیم. البته، حال ما به زبان Java کدنویسی خواهیم کرد.

واضح‌ترین راه حل برای این مشکل، این است که کارهای سنگین خود را خارج از بلوک try انجام دهید، اما این کار آنچنان باب میل نیست. جریان داده‌ها پیچیده می‌شود، و نمی‌توانید از const استفاده کنید، گرچه تنها ۱ وظیفه وجود دارد.

منطق استخراج شده از بلوک‌های try

async () => {

  let data;

  try {

    data = await fetchData();

  } catch (e) {

    return;

  }

  doSomethingComplex(data);

};

خواندن این کد آنچنان لذت‌بخش نیست و وقتی که موارد حاشیه‌ای‌تر را مدیریت می‌کنید، ناخوش‌آیند‌تر نیز می‌شود. همچنین این روش، ظرفیت بالایی برای در بر گرفتن خطاها در آینده را دارد.

منتظر یک promise ماندن این مشکل را بر طرف نمی‌کند، زیرا هنوز هنوز هم یک promise وجود دارد. می‌توانید خطاها را بدون منتظر promiseها ماندن مدیریت کنید.

Await به همراه .catch()

async () => {

  const data = await fetchData().catch(e => {

  });

  if (!data) return;

  doSomethingComplex(data);

};

این مورد به خوبی کار می‌کند، زیرا در اکثر مواقع، مدیریت خطا به طور نسبی خود مختار است. Case موفق شما هنوز هم بدون این که مدیریت خطا باعث شود ساختار کد عجیبی داشته باشید، از await سود می‌برد، اما لازم است که یک null check بر روی داده‌های خود انجام دهید. برای جریانات پیچیده‌تر async، فکر می‌کنم که خواندن این کد آسان‌تر می‌شود و نوشتن آن نیز، بصری‌تر می‌شود. فراموش کردن null checkها بسیار ساده است و ممکن است که باعث بروز باگ هایی شوند که در هنگام نوشتن جریانات پیچیده، از دست دادنشان آسان است.

در جهت جلوگیری از بروز باگ، پیشنهاد می‌کنم که بر روی هر چیزی که قرار است در مرورگر اجرا شود، از async / await استفاده نکنید.

پس promiseها چه می‌شوند؟

وقتی که با promiseها بدون async / await سر و کار دارید، انتخاب یک گزینه برای مدیریت خطا ساده‌تر است؛ زیرا فقط ۲ انتخاب وجود دارند: .catch()، یا آرگومان دوم .then().

promiseها به همراه .catch()

() => {

  fetchData()
    .then(data => {
      doSomethingComplex(data);
    })
    .catch(err => {
       // err
    });
};

این مورد نیز همان مشکل بلوک try / catch ما را دارد و بیش از حد خطاها را مدیریت می‌کند. در نهایت، وقتی که در هنگام ویرایش doSomethingComplex یک اشتباه تایپی بروز می‌دهد، وقتمان هدر می‌رود، زیرا خطا را نمی‌بینیم. در عوض، ترجیح می‌دهم که از آرگومان .then() استفاده کنم.

  fetchData()

    .then(

      data => {

        doSomethingComplex(data);

      },

      err => {

      }

    );

};

به ندرت پیش می‌آید که من از .catch() استفاده کنم. من می‌خواهم خطاهای موجود در مورد موفقیت‌آمیز من، تا جایی که بتوانم آن‌ها را ببینم، پخش شوند. در غیر این صورت، هر مشکلی در طی توسعه، در بر گرفته می‌شوند و احتمال این که بدون این که خودم بفهمم یک باگ در کد خود داشته باشم را افزایش می‌دهد.

گرچه، ترجیح می‌دهم که خطاها را با دقت بیشتری مدیریت کنم. ترجیح می‌دهم که باگ‌های سطحی داشته باشم تا بتوانم آن‌ها را ببینم و برطرف کنم. اگر می‌خواهید که رابط کاربری شما در هنگام مواجهه با هر خطا، همچنان به کار خود ادامه دهد، جلوگیری از پخش شدن خطاها می‌تواند مطلوب باشد. در نظر داشته باشید که انجام این کار، به این معنی است که فقط خطاهای بزرگ log می‌شوند.

مشکلات دیگر با promiseها

یکی از مواردی که در هنگام کار با promiseها دریافت کردم، خطای thrown errors within a promise will always cause a rejection است. اگر می‌خواهید چکیده‌ای از نوعی داده خارجی داشته باشد، این می‌تواند یک مشکل باشد.

const fetchData = () =>

  requestData().then(({ data }) =>

    data.map(removeUnusedFields)

  );

//

fetchData().then(handleSuccess, err => {

});

این فقط نحوه رفتار رایج promiseها است، اما برخی مواقع در هنگام توسعه در سد راه من قرار گرفته است. راه حل ساده‌ای برای آن وجود ندارد؛ پس فقط موقعیتی است که باید در طی توسعه در ذهن داشته باشید. احتمال بروز خود به خود آن در طی توسعه کم است، اما می‌تواند در هنگام ویرایش کد وقت شما را بگیرد.

منبع

مقالات پیشنهادی

نکات مثبت، اشتباهات و نحوه استفاده async/await در جاوااسکریپت

async/await که در ES7 معرفی شد، بهبود شگفت‌انگیزی در برنامه‌نویسی ناهمگام با JavaScript است. این نوع تابع، گزینه‌ای برای استفاده از کد همگام برای دستر...

مدیریت ارتباط با مشتری یا CRM چیست ؟

در فضای  کسب وکار هر شرکت و کسب و کاری که وارد بشوید مشاهده می کنید که در آنجا نرم افزار هایی متناسب با فعالیت آن کسب و کار در حال استفاده است. معمولا...

JavaScript ناهمگام با استفاده از Async / Await

Async / Await روش تقریبا جدیدی برای نوشتن کد ناهمگام در JavaScript است. قبل‌ها ما از callbackها و promiseها استفاده می‌کردیم. Async / Await در واقع بر...

افزایش سرعت وبسایت با استفاده از HTTP/2

HTTP/2 راهی جدید برای سریع‌تر کردن میزان زمان بارگذاری وبسایت با حذف کردن برخی از ویژگی‌های ناکارآمد در HTTP است. پیاده‌سازی HTTP/2 کار سختی نیست و نی...