برنامه نویسی

بررسی توابع AsyncAwait در جاوااسکریپت

مقدمه

Promiseها راه ساده‌تری برای مدیریت عملیات asynchronous در جاوااسکریپت فراهم کردند و باعث شدند بتوانیم کدها را به‌صورت ترتیبی و قابل‌فهم‌تر بنویسیم. از آنجایی که ذهن انسان برای مدیریت همزمانی‌های پیچیده طراحی نشده، این ویژگی کمک بزرگی محسوب می‌شود.

ویژگی async/await که در ES2017 (یا ES8) معرفی شد، کار را حتی ساده‌تر کرد. با استفاده از آن می‌توان کدی نوشت که ظاهری کاملاً synchronous دارد، در حالی که در پشت صحنه عملیات asynchronous انجام می‌شود.

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


مثال ساده

در مثال زیر، ابتدا تابعی داریم که یک Promise برمی‌گرداند و بعد از ۲ ثانیه مقدار 🤡 را resolve می‌کند:

function scaryClown() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('🤡');
    }, 2000);
  });
}

سپس یک تابع async تعریف می‌کنیم:

async function msg() {
  const msg = await scaryClown();
  console.log('Message:', msg);
}

msg();

خروجی بعد از ۲ ثانیه:

Message: 🤡

await چیست؟

کلمه کلیدی await برای صبر کردن روی resolve یا reject شدن یک Promise استفاده می‌شود.

نکته مهم:

  • await فقط داخل توابع async قابل استفاده است.

اجرای چند مرحله async

مثال زیر قدرت واقعی async/await را بهتر نشان می‌دهد:

function who() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('🤡');
    }, 200);
  });
}

function what() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('lurks');
    }, 300);
  });
}

function where() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('in the shadows');
    }, 500);
  });
}

تابع async:

async function msg() {
  const a = await who();
  const b = await what();
  const c = await where();

  console.log(`${a} ${b} ${c}`);
}

msg();

خروجی:

🤡 lurks in the shadows

بعد از حدود ۱ ثانیه.


اجرای موازی با Promise.all

در مثال قبلی، هر عملیات منتظر اتمام عملیات قبلی می‌ماند. اگر بخواهیم همه عملیات همزمان اجرا شوند، از Promise.all استفاده می‌کنیم:

async function msg() {
  const [a, b, c] = await Promise.all([
    who(),
    what(),
    where()
  ]);

  console.log(`${a} ${b} ${c}`);
}

اکنون خروجی بعد از حدود ۵۰۰ میلی‌ثانیه نمایش داده می‌شود.


Promise.all چگونه کار می‌کند؟

متد Promise.all:

  • چند Promise را به‌صورت همزمان اجرا می‌کند.
  • بعد از resolve شدن همه Promiseها، آرایه‌ای از نتایج برمی‌گرداند.

توابع async همیشه Promise برمی‌گردانند

مثال:

async function hello() {
  return 'Hello Alligator!';
}

const b = hello();

console.log(b);

خروجی:

Promise { ... }

چون تابع async همیشه Promise برمی‌گرداند.


دریافت مقدار Promise

hello().then(x => console.log(x));

خروجی:

Hello Alligator!

انواع مختلف تعریف async

تابع معمولی

async function msg() {
}

Function Expression

const msg = async function() {
}

Arrow Function

const msg = async () => {
}

مدیریت خطاها با try…catch

یکی از بهترین ویژگی‌های async/await مدیریت ساده خطاست.

مثال:

function yayOrNay() {
  return new Promise((resolve, reject) => {

    const val = Math.round(Math.random());

    val
      ? resolve('Lucky!!')
      : reject('Nope 😠');

  });
}

استفاده:

async function msg() {
  try {
    const msg = await yayOrNay();
    console.log(msg);
  } catch(err) {
    console.log(err);
  }
}

مزیت مهم

برخلاف callbackها یا chainهای طولانی Promise، مدیریت خطا در async/await بسیار شبیه کدنویسی عادی و synchronous است.


مدیریت خطا با catch

چون async همیشه Promise برمی‌گرداند:

msg().catch(err => console.log(err));

گرفتن خطاهای Runtime

function caserUpper(val) {
  return new Promise(resolve => {
    resolve(val.toUpperCase());
  });
}

تابع async:

async function msg(x) {
  try {
    const msg = await caserUpper(x);
    console.log(msg);
  } catch(err) {
    console.log('Ohh no:', err.message);
  }
}

اجرا:

msg('Hello'); // HELLO
msg(34);      // Error

خروجی:

Ohh no: val.toUpperCase is not a function

استفاده از Async/Await با Fetch API

متد fetch چون Promise-based است، گزینه‌ای عالی برای async/await محسوب می‌شود.

async function fetchUsers(endpoint) {

  const res = await fetch(endpoint);

  let data = await res.json();

  data = data.map(user => user.username);

  console.log(data);
}

استفاده:

fetchUsers('https://jsonplaceholder.typicode.com/users');

پشتیبانی مرورگرها

طبق آمار سال ۲۰۲۰:

  • حدود ۹۴٪ مرورگرها از async/await پشتیبانی می‌کنند.
  • مرورگرهای قدیمی مثل IE11 و Opera Mini از آن پشتیبانی نمی‌کنند.

جمع‌بندی

قبل از async/await، کدهای asynchronous در جاوااسکریپت معمولاً به چیزی معروف به:

Callback Hell

تبدیل می‌شدند؛ یعنی زنجیره‌ای پیچیده از callbackها که خواندن و نگهداری آن‌ها سخت بود.

اما async/await باعث شد:

  • کدها خواناتر شوند
  • مدیریت خطا ساده‌تر شود
  • ساختار کدها شبیه synchronous programming باشد
  • توسعه و دیباگ راحت‌تر شود

امروزه async/await یکی از مهم‌ترین مفاهیم مدرن جاوااسکریپت محسوب می‌شود و تقریباً در تمام پروژه‌های حرفه‌ای استفاده می‌شود.

از همراهی شما با پارمین کلود سپاسگزاریم.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا