برنامه نویسی و طراحی سایت

Async و Await در جاوا اسکریپت – توضیح به زبان ساده + مثال و کد

Async و Await در جاوا اسکریپت – توضیح به زبان ساده + مثال و کد

در زبان برنامه نویسی جاوا اسکریپت، کلمات کلیدی «Async» و «Await» «سینتکسی» (Syntax) جدید هستند که به مدیریت عملیات ناهمزمانی کمک می‌کنند. در این مطلب آموزش از مجله تم آف موضوع Async و Await در جاوا اسکریپت و ابعاد مختلف آن پوشش داده خواهد شد تا کاربران بتوانند در آخر عملیات ناهمزمانی را در جاوا اسکریپت یاد بگیرند.

فهرست مطالب این نوشته
ناهمزمانی در جاوا اسکریپت چیست؟

نحوه ایجاد تابع Async در جاوا اسکریپت

کلمه کلیدی Async در جاوا اسکریپت چیست؟

کلمه کلیدی Await در جاوا اسکریپت

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

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

تابع پیکان ناهمگام

Async و Await در جاوا اسکریپت بر اساس Promise

تغییر از Promise ها به Async و Await در جاوا اسکریپت

مدیریت خطا در جاوا اسکریپت ناهمگام چگونه است؟

استفاده از Catch برای مدیریت رد وعده

انجام همزمان عملیات ناهمزمان در جاوا اسکریپت

استفاده از انتظار ناهمزمان در حلقه های همزمان

انتظار سطح بالا در جاوا اسکریپت

سخن پایانی

faradars mobile

 ناهمزمانی در جاوا اسکریپت چیست؟

زبان برنامه نویسی جاوا اسکریپت شامل عملیات خاصی است که ناهمزمان هستند، به این معنی که مقدار یا نتیجه تولید شده فوراً در دسترس نخواهد بود، این موضوع به نام جاوا اسکریپت ناهمزمان شناخته می‌شود.

آموزش JavaScript ES6 جاوا اسکریپت
فیلم آموزش JavaScript ES6 جاوا اسکریپت در تم آف

کلیک کنید

قطعه کد زیر برای درک این موضوع است:

function fetchDataFromApi() {
  // Data fetching logic here
  console.log(data);
}

fetchDataFromApi();
console.log('Finished fetching data');

به دلیل ماهیت ناهمزمان تابع fetchDataFromApi

 ، مفسر جاوا اسکریپت قبل از ادامه دستورات بعدی، منتظر تکمیل آن نمی‌ماند. در نتیجه، قبل از ثبت اطلاعات واقعی به‌دست‌آمده از API، فرایند Finished Fetching

 را ثبت می‌کند. در بسیاری از شرایط، این عملیات مورد نظر نیست. خوشبختانه، می‌توان از ویژگی Async در جاوا اسکریپت استفاده کرد و منتظر این کلمات کلیدی ماند تا برنامه‌ قبل از ادامه، منتظر پایان عملیات ناهمزمان باشد. این ویژگی در «ES2017» به جاوا اسکریپت معرفی شد و توسط تمام مرورگرهای امروزی پشتیبانی می‌شود.

Async و Await در javascript

نحوه ایجاد تابع Async در جاوا اسکریپت

در این بخش از آموزش Async و Await در جاوا اسکریپت، منطق بازیابی اطلاعات تابع fetchDataFromApi

 عمیق‌تر مورد بررسی قرار خواهد گرفت. fetchDataFromApi

نمونه‌ای معمولی از عملیات ناهمزمان در جاوا اسکریپت است.

آموزش جاوا اسکریپت JavaScript
فیلم آموزش جاوا اسکریپت JavaScript در تم آف

کلیک کنید

همان‌طور که در کدهای زیر نشان داده شده، می‌توان از Fetch API در جاوا اسکریپت برای رسیدن به این هدف استفاده کرد:

function fetchDataFromApi() {
  fetch('https://v2.jokeapi.dev/joke/Programming?type=single')
    .then(res => res.json())
    .then(json => console.log(json.joke));
}

fetchDataFromApi();
console.log('Finished fetching data');

در سناریوی بالا، یک جوک برنامه نویسی از «JokeAPI» بازیابی شده است. پس از پاسخ API که با فرمت «JSON» خواهد بود، اطلاعات با استفاده از متد ‎ json()

 استخراج می‌شوند و سپس لطیفه مربوط به کنسول خروجی داده خواهد شد. توجه به این نکته ضروری است که از آنجایی که JokeAPI نوعی API شخص ثالث محسوب می‌شود، پس نمی‌توان از کیفیت جوک‌هایی که برمی‌گرداند اطمینان حاصل کرد. اگر این کد در مرورگر یا در «نود جی اس» (Node.js) اجرا شود، امکان دارد که خروجی کنسول هنوز ترتیب درستی نداشته باشد.

جاوا اسکریپت ناهمگام

کلمه کلیدی Async در جاوا اسکریپت چیست؟

برای فعال کردن رفتار ناهمزمان در توابع جاوا اسکریپت، می‌توان از کلمه کلیدی Async قبل از کلمه کلیدی تابع استفاده کرد. برای مثال، اجازه دهید تابعی به نام fetchDataFromApi()‎

 را در نظر بگیریم که داده‌ها را از یک API واکشی می‌کند.

آموزش پروژه محور جاوا اسکریپت، CSS و HTML – طراحی صفحه فرود واکنشگرا
فیلم آموزش پروژه محور جاوا اسکریپت، CSS و HTML – طراحی صفحه فرود واکنشگرا در تم آف

کلیک کنید

همان‌طور که در ادامه نشان داده شده است، می‌توان با استفاده از کلمه کلیدی Async در جاوا اسکریپت آن را ناهمزمان کرد:

async function fetchDataFromApi() {
  fetch('https://v2.jokeapi.dev/joke/Programming?type=single')
    .then(res => res.json())
    .then(json => console.log(json.joke));
}

وقتی تابعی به عنوان ناهمزمان علامت‌گذاری می‌شود، همیشه «وعده» (Promise) را برمی‌گرداند. می‌توان متد then()

 را به فراخوانی تابع زنجیره زد تا اطمینان حاصل شود که ترتیب اجرا درست است. برای مثال، می‌توان تابع fetchDataFromApi()‎

را فراخوانی کرد و سپس متد then()

را مانند کد زیر به آن زنجیره زد:

fetchDataFromApi()
  .then(() => {
    console.log('Finished fetching data');
  });

پس از اجرای کد، داده‌های واکشی شده و پیام ‘Finished fetching data’

 به عنوان خروجی دریافت خواهند شد و چیزی مشابه خروجی زیر را بیرون خواهد داد:

If Bill Gates had a dime for every time Windows crashed ... Oh wait, he does.
Finished fetching data

 

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

 Await در جاوا اسکریپت

کلمه کلیدی Await در جاوا اسکریپت

برای اینکه عملیات ناهمزمان را متوقف کنیم و منتظر نتیجه بمانیم، باید از کلمه کلیدی Await در مقابل آن عملیات در تابع استفاده کرد. سپس می‌توان نتایج این عملیات را به متغیرها اختصاص داد.

آموزش آلپاین در جاوا اسکریپت – فریم ورک Alpine در JavaScript
فیلم آموزش آلپاین در جاوا اسکریپت – فریم ورک Alpine در JavaScript در تم آف

کلیک کنید

برای مثال، اجازه دهید تابع fetchDataFromApi()‎

را که قبلاً به عنوان ناهمزمان علامت‌گذاری کرده بودیم در نظر بگیریم. می‌توان از کلمه کلیدی Await برای توقف اجرا تا زمانی استفاده کرد که نتیجه فراخوانی API دریافت می‌شود و نتیجه را به یک متغیر اختصاص می‌دهد. سپس می‌توان از کلمه کلیدی Await دیگری برای توقف اجرا استفاده کرد و منتظر پاسخ JSON از تماس API ماند. در نهایت، می‌توان ویژگی joke

 پاسخ JSON را به کنسول وارد کرد. در اینجا سینتکس تغییر تابع fetchDataFromApi()‎

با استفاده از کلمه کلیدی Await آمده است:

async function fetchDataFromApi() {
  const res = await fetch('https://v2.jokeapi.dev/joke/Programming?type=single');
  const json = await res.json();
  console.log(json.joke);
}

همچنین باید منتظر نتیجه فراخوانی تابع fetchDataFromApi()‎

ماند که قطعه کد آن به صورت زیر است:

await fetchDataFromApi();
console.log('Finished fetching data');

برای اطمینان از صحیح بودن دستور اجرا، باید منتظر نتیجه فراخوانی تابع fetchDataFromApi()‎

بمانیم. می‌توان با استفاده از کلمه کلیدی Await دوباره به این هدف رسید. با این حال، اگر کاربر بخواهد از انتظار خارج از تابعی ناهمزمان در اسکریپت غیر ماژول استفاده کند، با خطای زیر مواجه خواهد شد.

Uncaught SyntaxError: await is only valid in async functions, async generators and modules

 

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

async function fetchDataFromApi() {
  const res = await fetch('https://v2.jokeapi.dev/joke/Programming?type=single');
  const json = await res.json();
  console.log(json.joke);
}

async function init() {
  await fetchDataFromApi();
  console.log('Finished fetching data');
}

init();

اکنون اگر کدها اجرا شوند، همه چیز باید به ترتیب درست مانند خروجی زیر نمایش داده شود:

UDP is better in the COVID era since it avoids unnecessary handshakes.
Finished fetching data

ترتیب درست اجرا برای Async و Await در جاوا اسکریپت بسیار حائز اهمیت است.

توابع ناهمگام در جاوا اسکریپت

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

روش‌های مختلفی برای تعریف توابع ناهمگام، فراتر از دو اعلان تابع نام‌گذاری شده (با استفاده از کلمه کلیدی تابع و نام تابع) وجود دارند که در مثال قبلی ارائه شدند. همچنین می‌توان نشان داد که «عبارات تابع» (Function Expression)، «توابع پیکان» (Arrow Function) و «توابع ناشناس» (Anonymous Function) ناهمزمان هستند.

آموزش JavaScript ES6 جاوا اسکریپت
فیلم آموزش JavaScript ES6 جاوا اسکریپت در تم آف

کلیک کنید

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

عبارت تابع یا Function Expression به ایجاد تابع و انتساب آن به یک متغیر اشاره دارد. تابع ایجاد شده ناشناس است، به این معنی که نامی ندارد. مثال زیر این مفهوم را بیان می‌کند:

const fetchDataFromApi = async function() {
  const res = await fetch('https://v2.jokeapi.dev/joke/Programming?type=single');
  const json = await res.json();
  console.log(json.joke);
}

این کد مانند مثال قبلی عمل می‌کند.

تابع پیکان ناهمگام

در «جاوا اسکریپت ES6»، توابع پیکان به عنوان نوعی جایگزین مختصر برای عبارات تابع معرفی شدند و همیشه ناشناس هستند.

آموزش JavaScript ES6 جاوا اسکریپت
فیلم آموزش JavaScript ES6 جاوا اسکریپت در تم آف

کلیک کنید

سینتکس اصلی آن‌ها به صورت زیر است:

(params) => {  }

برای نشان دادن ناهمزمان بودن تابع پیکان، کلمه کلیدی Async را قبل از پرانتز باید نوشت. به عنوان مثال، به جای ایجاد یک تابع init

 اضافی در کد قبلی، می‌توان کد موجود را در نوعی IIFE

 قرار داد که این IIFE

به‌ عنوان Async علامت‌گذاری شده است. مثال زیر این موضوع را بیان خواهد کرد:

(async () => {
  async function fetchDataFromApi() {
    const res = await fetch('https://v2.jokeapi.dev/joke/Programming?type=single');
    const json = await res.json();
    console.log(json.joke);
  }
  await fetchDataFromApi();
  console.log('Finished fetching data');
})();

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

آموزش پروژه محور جاوا اسکریپت، CSS و HTML – طراحی صفحه فرود واکنشگرا
فیلم آموزش پروژه محور جاوا اسکریپت، CSS و HTML – طراحی صفحه فرود واکنشگرا در تم آف

کلیک کنید

علاوه بر این، در توابع معمولی، کلمه کلیدی This در جاوا اسکریپت به صورت پویا و بر اساس نحوه فراخوانی تابع محدود می‌شود، در حالی که توابع پیکان این اتصال خاص را ندارند. در عوض، آن‌ها This را از محدوده محصور به ارث می‌برند که گاهی اوقات می‌تواند منجر به رفتار غیرمنتظره شود.

کار با Async و Await در جاوا اسکریپت

Async و Await در جاوا اسکریپت بر اساس Promise

Async و Await در جاوا اسکریپت بیشتر شکل مختصری برای وعده‌ها محسوب می‌شوند. درک نحوه استفاده از «وعده‌ها در پس‌زمینه» (promises under the hood) بسیار مهم است.

آموزش مقدماتی کتابخانه ReactJS در جاوا اسکریپت
فیلم آموزش مقدماتی کتابخانه ReactJS در جاوا اسکریپت در تم آف

کلیک کنید

توابع Async همیشه وعده‌ها را بازمی‌گردانند، حتی اگر به صراحت مشخص نشده باشند. مثال زیر این مفهوم را بیان می‌کند:

async function echo(arg) {
  return arg;
}

const res = echo(5);
console.log(res);

خروجی این قطعه کد به صورت زیر خواهد بود:

Promise { : "fulfilled", : 5 }

 

حالت promise در جاوا اسکریپت می‌تواند یکی از سه گزینه ممکن، «در حال انتظار» (Pending)، «محقق شده» (Fulfilled) یا «رد شده» (Rejected) باشد. وعده در ابتدا در حالت معلق شروع می‌شود. در صورتی که عملیاتی که وعده بیانگر آن است موفقیت‌آمیز باشد، وعده محقق شده تلقی خواهد شد. بالعکس، در صورتی که عملیاتی که وعده بیانگر آن است موفقیت آمیز نباشد، وعده رد شده خواهد بود. هنگامی که وعده، محقق یا رد می‌شود، به عنوان «تسویه شده» (Settled) طبقه‌بندی خواهد شد، به این معنی که نمی‌تواند به هیچ حالت دیگری منتقل شود.

promise در JavaScript

وقتی که از کلمه کلیدی Await در تابع Async برای توقف اجرای آن استفاده می‌شود، کاری که در واقع انجام خواهد شد این است که کاربر منتظر وعده‌ای خواهد ماند که ممکن است آن وعده صریح یا ضمنی باشد و آن را در حالت محقق شده یا رد شده قرار دهد. با تکیه بر مثال بالا، کارهای گفته شده را می‌توان در مثال زیر بیان کرد.

async function echo(arg) {
  return arg;
}

async function getValue() {
  const res = await echo(5);
  console.log(res);
}

getValue();
// 5

قطعه کد بالا نوعی تابع ناهمزمان در جاوا اسکریپت را نشان می‌دهد. تابع echo

 با کلمه کلیدی Async اعلان می‌شود و یک وعده یا همان Promise را برمی‌گرداند. همچنین وقتی تابع getValue

 فراخوانی می‌شود، از کلمه کلیدی Await استفاده می‌کند تا قبل از چاپ نتیجه در کنسول منتظر بماند تا وعده‌ای که توسط echo

برگردانده می‌شود حل شود.

Promises نوعی ویژگی مهم در جاوا اسکریپت برای مدیریت عملیات ناهمزمان است. آن‌ها به‌طور گسترده توسط APIهای مرورگر جدیدتر مانند «Battery status API»، «Clipboard API»، «Fetch API» و «MediaDevices API» استفاده می‌شوند.

آموزش ویو جی اس – فریم ورک Vue.js در جاوا اسکریپت JavaScript
فیلم آموزش ویو جی اس – فریم ورک Vue.js در جاوا اسکریپت JavaScript در تم آف

کلیک کنید

همچنین Node.js با تابع util.promisify

 داخلی خود از وعده‌ها پشتیبانی می‌کند، که این تابع می‌تواند توابعی را که از callback

 استفاده می‌کنند به وعده‌های بازگشتی تبدیل کند. علاوه بر این، با استفاده از «Node.js v10» یا نود جی اس ورژن ١٠ توابع در ماژول fs

 می‌توانند مستقیماً وعده‌ها را برگردانند.

مطلب پیشنهادی:

برنامه نویسی ناهمگام جاوا اسکریپت با Promise ها — راهنمای کاربردی

شروع مطالعه

 

تغییر از Promise ها به Async و Await در جاوا اسکریپت

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

آموزش JavaScript ES6 جاوا اسکریپت
فیلم آموزش JavaScript ES6 جاوا اسکریپت در تم آف

کلیک کنید

قبلاً بیان شد که چگونه می‌توان تماس واکشی مبتنی بر وعده را تغییر داد تا بتوان با Async و Await در جاوا اسکریپت کار کرد. در اینجا مثالی دیگر برای این کار بررسی خواهد شد. در مثال زیر نوعی تابع برای دریافت محتویات فایلی با استفاده از API مبتنی بر وعده Node و روش readFile

 آوده شده است. قطعه کد این کار با استفاده از Promise.then()‎

 به صورت زیر خواهد بود:

const { promises: fs } = require('fs');

const getFileContents = function(fileName) {
  return fs.readFile(fileName, enc)
}

getFileContents('myFile.md', 'utf-8')
  .then((contents) => {
    console.log(contents);
  });

حال همان قطعه کد با استفاده از Async و Await در جاوا اسکریپت به صورت زیر خواهد بود:

import { readFile } from 'node:fs/promises';

const getFileContents = function(fileName, enc) {
  return readFile(fileName, enc)
}

const contents = await getFileContents('myFile.md', 'utf-8');
console.log(contents);

نکته: کدهای بالا در واقع استفاده از نوعی ویژگی به نام «Top-Level-Await» به معنای انتظار سطح بالا، است که فقط در ماژول‌های ES وجود دارد. برای اجرای این کد، باید فایل را به صورت Index.mjs ذخیره و از نسخه ١٤.٨ به بالای نود‌جی‌اس استفاده کرد. اگر چه این‌ها نمونه‌های ساده‌ای هستند، اما سینتکس Async و Await در جاوا اسکریپت چندان هم دشوار نیست.

برنامه ناهمگام در JS

مدیریت خطا در جاوا اسکریپت ناهمگام چگونه است؟

توابع Async راه‌های مختلفی را برای رسیدگی به خطاها ارائه می‌دهند. یکی از متداول‌ترین راه‌ها استفاده از بلوک «try…catch»  برای کار با عملیات ناهمزمان و گرفتن هر گونه خطای رخ داده است.

آموزش جاوا اسکریپت JavaScript
فیلم آموزش جاوا اسکریپت JavaScript در تم آف

کلیک کنید

در مثال زیر، باید توجه کرد که چگونه URL به چیزی که وجود ندارد تغییر یافته است.

sync function fetchDataFromApi() {
  try {
    const res = await fetch('https://non-existent-url.dev');
    const json = await res.json();
    console.log(json.joke);
  } catch (error) {
    // Handle the error here in whichever way you like
    console.log('Something went wrong!');
    console.warn(error)
  }
}

await fetchDataFromApi();
console.log('Finished fetching data');

خروجی این مثال به صورت زیر خواهد بود.

Something went wrong!
TypeError: fetch failed
    ...
    cause: Error: getaddrinfo ENOTFOUND non-existent-url.dev
Finished fetching data

 

اگر عملیات واکشی ناموفق باشد، متد «رد وعده» فراخوانی می‌شود و کلمه کلیدی Await آن وعده رد شده کنترل نشده را به نوعی خطای قابل دریافت تبدیل می‌کند. در این مورد، پیام Something went wrong

 همراه با «خطای نوع» (TypeError) که نشان می‌دهد واکشی ناموفق است به کنسول وارد می‌شود. در نهایت، کنسول Finished Fetching Data

 را ثبت می‌کند. بلوک try…catch

 به کاربر این امکان را می‌دهد که خطاها را به خوبی مدیریت کند، بدون اینکه باعث از کار افتادن برنامه شود.

روش فوق برای رسیدگی به خطاها در توابع همگام چند مشکل دارد. اولاً، می‌تواند به کدهای شلوغ و پیچیده منجر شود. برای مثال در ساخت برنامه «CRUD» با عملکردهای جداگانه برای ایجاد، خواندن و به‌روزرسانی عملیات نیاز خواهد بود که هر کدام شامل یک فراخوانی API ناهمزمان است و هر تماس باید در بلوک try…catch

منحصربه‌فرد خودش نوشته شود که این کار منجر به افزایش چشم‌گیر کدها خواهد شد. موضوع دیگر این است که در صورت عدم استفاده از کلمه کلیدی Await، رد وعده لغو می‌شود. مثال زیر برای درک این موضوع مهم است:

import { readFile } from 'node:fs/promises';

const getFileContents = function(fileName, enc) {
  try {
    return readFile(fileName, enc)
  } catch (error) {
    console.log('Something went wrong!');
    console.warn(error)
  }
}

const contents = await getFileContents('this-file-does-not-exist.md', 'utf-8');
console.log(contents);

خروجی این مثال به صورت زیر است:

node:internal/process/esm_loader:91
    internalBinding('errors').triggerUncaughtException(
                              ^
[Error: ENOENT: no such file or directory, open 'this-file-does-not-exist.md'] {
  errno: -2,
  code: 'ENOENT',
  syscall: 'open',
  path: 'this-file-does-not-exist.md'
}

 

بر خلاف کلمه کلیدی Await، کلمه کلیدی «Return»، رد وعده‌ها را به خطاهای قابل دریافت تبدیل نمی‌کند.

استفاده از Catch برای مدیریت رد وعده

هر تابعی که وعده‌ای را برمی‌گرداند می‌تواند از متد catch()

 برای رسیدگی به رد وعده‌هایی استفاده کند که ممکن است رخ دهند. این رویکرد مختصرتر از استفاده از بلوک‌های try…catch

برای هر تماس ناهمزمان است.

آموزش پروژه محور جاوا اسکریپت، CSS و HTML – طراحی صفحه فرود واکنشگرا
فیلم آموزش پروژه محور جاوا اسکریپت، CSS و HTML – طراحی صفحه فرود واکنشگرا در تم آف

کلیک کنید

با پیاده‌سازی متد catch()

در مثال بالا، می‌توان به‌خوبی خطا را مطابق قطعه کد زیر مدیریت کرد:

const contents = await getFileContents('this-file-does-not-exist.md', 'utf-8')
  .catch((error) => {
    console.log('Something went wrong!');
    console.warn(error);
  });
console.log(contents);

خروجی این قطعه کد به صورت زیر خواهد بود:

Something went wrong!
[Error: ENOENT: no such file or directory, open 'this-file-does-not-exist.md'] {
  errno: -2,
  code: 'ENOENT',
  syscall: 'open',
  path: 'this-file-does-not-exist.md'
}
undefined

 

در این رابطه، «والری کارپوف» (Valeri Karpov) با توجه به اینکه کدام رویکرد باید استفاده شود، استفاده از بلوک‌های try…catch

را برای بازیابی خطاهای مورد انتظار در توابع Async پیشنهاد کرد. همچنین برای خطاهای غیرمنتظره، توصیه می‌شود با افزودن catch()

به تابع فراخوانی، آن را مدیریت کنیم.

عملیات ناهمزمان در JavaScript

انجام همزمان عملیات ناهمزمان در جاوا اسکریپت

وقتی از کلمه کلیدی Await برای منتظر ماندن برای تکمیل عملیات ناهمزمان استفاده می‌شود، مفسر جاوا اسکریپت اجرا را تا زمانی متوقف می‌کند که عملیات کامل شود. با این حال، مواقعی وجود دارد که این رفتار مناسب نیست.

آموزش JavaScript ES6 جاوا اسکریپت
فیلم آموزش JavaScript ES6 جاوا اسکریپت در تم آف

کلیک کنید

کد زیر برای درک این موضوع مهم است:

(async () => {
  async function getStarCount(repo){
    const repoData = await fetch(repo);
    const repoJson = await repoData.json()
    return repoJson.stargazers_count;
  }

  const reactStars = await getStarCount('https://api.github.com/repos/facebook/react');
  const vueStars = await getStarCount('https://api.github.com/repos/vuejs/core');
  console.log(`React has ${reactStars} stars, whereas Vue has ${vueStars} stars`)
})();

مثال بالا در حال انجام دو تماس API برای دریافت تعداد ستاره‌های «GitHub» برای دو فریمورک «React» و «Vue» است. این کد به خوبی کار می‌کند و نیازی به انتظار برای اولین وعده حل شده قبل از درخواست واکشی دوم وجود ندارد، اما اگر درخواست‌های زیادی وجود داشته باشند، اجرای کد با مشکل مواجه خواهد شد.

برای حل این مشكل، می‌توان از Promise.all

 استفاده کرد. Promise.all در جاوا اسکریپت مجموعه‌ای از وعده‌ها را می‌گیرد و منتظر می‌ماند تا همه وعده‌ها حل شوند یا هر یک از آن‌ها رد شوند که قطعه کد زیر برای بیان این موضوع است:

(async () => {
  async function getStarCount(repo){
    // As before
  }

  const reactPromise = getStarCount('https://api.github.com/repos/facebook/react');
  const vuePromise = getStarCount('https://api.github.com/repos/vuejs/core');
  const [reactStars, vueStars] = await Promise.all([reactPromise, vuePromise]);

  console.log(`React has ${reactStars} stars, whereas Vue has ${vueStars} stars`);
})();

استفاده از انتظار ناهمزمان در حلقه های همزمان

استفاده از انتظار ناهمزمان در حلقه‌های همزمان ممکن است زمانی مورد نیاز باشد که لازم باشد تابعی ناهمزمان را در حلقه همزمان فراخوانی کنیم.

آموزش پروژه محور جاوا اسکریپت، CSS و HTML – طراحی صفحه فرود واکنشگرا
فیلم آموزش پروژه محور جاوا اسکریپت، CSS و HTML – طراحی صفحه فرود واکنشگرا در تم آف

کلیک کنید

مثال زیر این موضوع را بیان می‌کند:

// Return promise which resolves after specified no. of milliseconds
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

async function process(array) {
  array.forEach(async (el) => {
    await sleep(el); // we cannot await promise here
    console.log(el);
  });
}

const arr = [3000, 1000, 2000];
process(arr);

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

1000
2000
3000

 

همین امر در مورد سایر متدهای آرایه در جاوا اسکریپت از جمله «نقشه» (Map)، «فیلتر» (Filter) و «کاهش» (Reduce) نیز صدق می‌کند. خوشبختانه، «ES2018» «تکرارکننده‌های» (Iterators) ناهمزمان را معرفی کرد که مانند تکرارکننده‌های معمولی رفتار می‌کنند، اما وعده‌ای را از متد next()

 خود بازمی‌گردانند. این ویژگی امکان استفاده از تابع Await را در آن‌ها فراهم می‌کند. حال در زیر برای درک بهتر کد بالا با استفاده از یکی از این تکرارکننده‌های جدید (در اینجا تکرار کننده for…of

 ) بازنویسی خواهد شد:

async function process(array) {
  for (el of array) {
    await sleep(el);
    console.log(el);
  };
}

اکنون تابع process

 همه چیز را به ترتیب صحیح در خروجی نشان می‌دهد:

3000
1000
2000

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

در عوض، بهتر است تمام وعده‌ها را یک‌باره خارج از حلقه ایجاد و سپس از Promise.all()‎

 استفاده کرد تا قبل از ادامه، منتظر ماند تا همه آن‌ها تکمیل شوند. این به برنامه کمک می‌کند تا از مسدود کردن حلقه رویداد جلوگیری کرده و عملکرد کلی برنامه را بهبود بخشد. حتی نوعی قانون «ESLint» برای این مسئله وجود دارد که به کاربر در مورد این رفتار هشدار می‌دهد.

انتظار سطح بالا در جاوا اسکریپت

اگر از کلمه کلیدی Await خارج از تابع Async در جاوا اسکریپت استفاده شود، خطای زیر دریافت خواهد شد.

Uncaught SyntaxError: await is only valid in async functions, async generators and modules

 

قطعه کد زیر این موضوع را نشان می‌دهد:

const ms = await Promise.resolve('Hello, World!');
console.log(msg)

این مشکل با معرفی نوعی ویژگی به نام انتظار سطح بالا در «ECMAScript 2022» برطرف شده و از نسخه ۱۴.۸ به بعد نود جی اس در دسترس است.

انتظار سطح بالا در JavaScript

«انتظار سطح بالا» (Top-level Await) به کاربر امکان می‌دهد از کلمه کلیدی Await در سطح بالای کد خود استفاده کند. با این حال، این ویژگی فقط در ماژول‌های ES معتبر است. برای استفاده از آن در مرورگر، می‌توان یک فایل index.js ایجاد کرد و آن را با تگ script

 مانند زیر در صفحه قرار داد.


در نود جی اس، می‌توان فایلی را به عنوان ماژول «ES» ساخت و آن را پسوند «mjs.» ذخیره کرد. بعد می‌توان اعلان آن را با دستور نود جی اس زیر انجام داد:

node index.mjs

همچنین، می‌توان فیلد type

 را روی module

 در فایل «package.json» مانند کدهای زیر قرار داد.

{
  "name": "myapp",
  "type": "module",
  ...
}

انتظار سطح بالا در مسائل مربوط به Async و Await در جاوا اسکریپت با «ایمپورت‌های پویا» (Dynamic Imports) به خوبی کار می‌کند و این به کاربر امکان می‌دهد ماژول‌های ES را به صورت ناهمزمان بارگیری کند. همچنین کاربر می‌تواند از نوعی عبارت تابع مانند برای وارد کردن پویای ماژول بر اساس برخی شرایط استفاده کند. مثال زیر برای بیان این موضوع است.

const locale = 'DE';

const { default: greet } = await import(
  `${ locale === 'DE' ?
      './de.js' :
      './en.js'
  }`
);

greet();
// Outputs "Hello" or "Guten Tag" depending on the value of the locale variable

ویژگی فوق می‌تواند به ویژه برای «بارگذاری با تاخیر» (Lazy Load) در ارتباط با فریمورک‌هایی مانند «React» و «Vue» مفید باشد. با استفاده از ویژگی وارد کردن پویا، می‌توان با کاهش مقدار کدی که در ابتدا باید بارگذاری شود، زمان تعاملی شدن برنامه را بهبود بخشید، به این معنی که کاربران می‌توانند زودتر از آن برنامه استفاده کنند.

مجموعه آموزش جاوا اسکریپت (JavaScript)
فیلم مجموعه آموزش جاوا اسکریپت (JavaScript) در تم آف

کلیک کنید

سخن پایانی

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

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

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

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.