جاوا اسکریپت Async: آشنایی با Async و Await (قسمت پایانی)

جاوا اسکریپت Async: آشنایی با Async و Await (قسمت پایانی)

جاوا اسکریپت Async: آشنایی با Async و Await (قسمت پایانی)

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

ویژگی که در این جلسه می خواهیم در مورد آن صحبت کنیم، Async و Await است که جزئی از استاندارد ES7 هستند (سال 2016 میلادی). از آنجایی که این تکنولوژی نسبتا جدید است احتمالا در برخی از مرورگرها پشتیبانی نمی شود ولی مرورگرهایی مثل کروم تا به حال آن را به صورت کامل پیاده سازی کرده اند. بنابراین جنبه ی اصلی بحث امروز یادگیری این تکنولوژی های جدید و استفاده از امکانات آن ها در آینده است ولی اگر می خواهید در حال حاضر از استانداردهای ES7 و جدیدتر استفاده کنید بهتر است کدهای جاوا اسکریپت خود را با استفاده از Babel و webpack و امثال آن ها کامپایل کرده و سپس در اختیار کاربر قرار دهید. من سعی می کنم در مقالاتی جداگانه از این مجموعه مقالات بحث کار با babel را نیز مطرح کنم.

فعلا می دانیم که این قابلیت در کروم پیاده سازی شده است و ما می توانیم برای تمرین این جلسه از آن استفاده کنیم بنابراین برای شروع کار یک فایل به نام index.html با محتوای زیر خواهیم داشت:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>JavaScript Sandbox</title>
</head>
<body>
  
  <script src="app.js"></script>
</body>
</html>

یک صفحه ی وب خالی که فایل app.js مان را به آن متصل کرده ایم. احتمالا برایتان روشن شده است که فایلی به نام app.js نیز خواهیم داشت بنابراین آن را نیز بسازید. برای شروع به فایل app.js رفته و یک تابع ساده تعریف کنید:

function myfunc() {
    return 'Hello';
}

console.log(myfunc());

همه ی ما می دانیم که با اجرای کد بالا رشته ای ساده حاوی Hello در کنسول مرورگر چاپ می شود. برای تبدیل کردن این تابع به یک تابع async تنها کافی است که کلیدواژه ی Async را در ابتدای آن قرار دهیم:

async function myfunc() {
    return 'Hello';
}

console.log(myfunc());

توابعی که Async باشند به صورت خودکار یک promise را بر می گردانند. خروجی کد بالا به شکل زیر است:

برگشتن promise به جای رشته
برگشتن promise به جای رشته

حالا دیگر خروجی یک رشته نیست بلکه یک promise است. حالا که تابع ما یک promise را بر می گرداند می توانیم از then روی آن استفاده کنیم و رشته ی Hello را دریافت کنیم:

async function myfunc() {
    return 'Hello';
}

myfunc()
    .then(res => console.log(res));

حالا که کدهایمان به صورت نامتقارن هستند می توانیم از قابلیت await استفاده کنیم تا بگویید تا رسیدن پاسخ از سمت سرور صبر کند. کاری شبیه به کارهایی که در جلسات کار با AJAX انجام می دادیم. من وضعیت نامتقارن سرور را با setTimeout شبیه سازی می کنم:

async function myFunc() {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve('Hello'), 1000);
  });

  const res = await promise;

  return res;
}

همانطور که می بینید در کار با promise ها می توانیم به سادگی نتیجه را به همراه await بیاوریم تا بر خلاف جلسات قبل، متغیر res برابر undefined نشود. با اجرای کد بالا پس از گذشت 1 ثانیه، رشته ی hello را دریافت می کنیم. اگر متغیر خودمان را بدون await می آوردیم با همان مشکل جلسات گذشته روبرو می شدیم: پاسخ دیر از زمان اجرای کد ارسال می شد بنابراین جاوا اسکریپت که هیچ پاسخی از سمت سرور ندارد، مقدار res را روی undefined قرار می دهد.

به طور مثال بیایید یک خطا را شبیه سازی کنیم تا ببینیم با خطاها چطور برخورد می کند:

async function myFunc() {
    const promise = new Promise((resolve, reject) => {
        setTimeout(() => resolve('Hello'), 1000);
    });

    const error = true;

    if (!error) {
        const res = await promise; // Wait until promise is resolved
        return res;
    } else {
        await Promise.reject(new Error('Something went wrong'));
    }
}

در همان اول متغیری تعریف کرده ام که error نام دارد و کارش شبیه سازی یک خطا در کد ما است. سپس در یک شرط if وجود یا عدم وجود خطا را بررسی کرده ایم، در صورتی که خطایی وجود نداشت نتیجه (res) را بر می گردانیم و در صورتی که خطا داشته باشیم یک promise جدید را برمی گردانیم. چرا؟ به دلیل اینکه می خواهیم بعدا در هنگام صدا زدن این تابع از catch استفاده کنیم و catch هم فقط روی promise ها اجرا خواهد شد.

در نهایت تابع را صدا زده و خطا های احتمالی را نیز می گیریم:

myFunc()
    .then(res => console.log(res))
    .catch(err => console.log(err));

با صدا زدن تابع و استفاده از then و catch مطمئن می شویم که کدها به درستی اجرا شوند. با اجرای کد بالا در مرورگر پیام something went wrong را خواهیم دید. اگر مقدار ثابت error را نیز روی false بگذاریم همان رشته ی Hello را دریافت خواهیم کرد.

البته شما این حالت از کدنویسی را کمتر مشاهده خواهید کرد. در اکثر اوقات async و await به همراه Fetch API مورد استفاده قرار می گیرد بنابراین من هم مثالی از Fetch API می زنم:

async function getUsers() {
  // await response of the fetch call
  const response = await fetch('https://jsonplaceholder.typicode.com/users');

  // Only proceed once its resolved
  const data = await response.json();

  // only proceed once second promise is resolved
  return data;
}

getUsers().then(users => console.log(users));

این مثال بهترین حالت استفاده از await و async است. در تابع بالا که async است ابتدا از دستور fetch استفاده کرده ایم تا users را از سرور jsonplaceholder دریافت کنیم. در حالت عادی باید از دستورات then استفاده می کردیم تا به مشکل برخورد نکنیم اما حالا با یک await ساده کد را نامتقارن می کنیم. همچنین دیگر نیازی به دستورات then برای دریافت داده و تبدیل آن از json نداریم بلکه با دستور await به صورت مستقیم به آن دسترسی خواهیم داشت. در نهایت با صدا زدن این تابع و console.log کردن کاربران به راحتی کد خود را تست کرده ایم:

برگشت داده شدن کاربران از سرور تمرینی
برگشت داده شدن کاربران از سرور تمرینی

بنابراین هیچ مشکلی در دریافت کاربران نداشته ایم.

به جهت راحت تر شدن کار شما من کتابخانه ی easyHTTP را با استفاده از await و async بازنویسی کرده ام و کد آماده شده ی آن را در اختیار شما قرار می دهم. مبحث await و async بسیار آسان هستند بنابراین نیازی به توضیحات اضافه نمی بینم:

class EasyHTTP {
  // Make an HTTP GET Request 
  async get(url) {
    const response = await fetch(url);
    const resData = await response.json();
    return resData;
  }

  // Make an HTTP POST Request
  async post(url, data) {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-type': 'application/json'
      },
      body: JSON.stringify(data)
    });

    const resData = await response.json();
    return resData;

  }

  // Make an HTTP PUT Request
  async put(url, data) {
    const response = await fetch(url, {
      method: 'PUT',
      headers: {
        'Content-type': 'application/json'
      },
      body: JSON.stringify(data)
    });

    const resData = await response.json();
    return resData;
  }

  // Make an HTTP DELETE Request
  async delete(url) {
    const response = await fetch(url, {
      method: 'DELETE',
      headers: {
        'Content-type': 'application/json'
      }
    });

    const resData = await 'Resource Deleted...';
    return resData;
  }

}

همانطور که مشاهده می کنید کدها چندین بار تمیزتر و خواناتر از حالت قبلی خود هستند! امیدوارم از قسمت پایانی دوره ی جاوا اسکریپت Async استفاده کرده باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری جاوا اسکریپت نامتقارن توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (2 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

دولوپر
24 آذر 1401
یعنی می تونم بگم فوق العاده بود...یکی از باکیفیت ترین محتواها رو در زمینه کار با API ها خوندم و در عین حال بسیار ساده و روان بود...باتشکر فراوان از زحمات شما و آرزوی موفقیت بیش از پیش...

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

محمدرضا
15 دی 1398
عالی بود

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.