درک برنامه‌نویسی Async/Await از طریق یک مثال عملی

ترجمه و تالیف : ارسطو عباسی
تاریخ انتشار : 29 شهریور 98
خواندن در 3 دقیقه
دسته بندی ها : جاوا اسکریپت

 

Async/Await راهی جدید برای نوشتن برنامه‌های asynchronous است که براساس قابلیت Promise نوشته شده، از این رو باید گفت که قابلیت non-Blocking نیز در این تکنیک وجود دارد. تنها تفاوتی که در این ساختار جدید نسبت به سینتکس قبلی یعنی ES6 وجود دارد آن است که تکنیک Async/Await سینتکس مشابه‌تری با دیگر روند‌های برنامه‌نویسی به خصوص synchronous داشته و به همین دلیل مورد استقبال بیشتری قرار گرفته است. 

اگر بخواهیم به صورت سریع با روند‌های قبلی که در آن سعی برای ساخت یک برنامه asynchronous داشتیم را بررسی کنیم باید Callback و Promise را بررسی نماییم. 

قبل از آن اگر قصد مطالعه بیشتری در ارتباط با جزئیات این موارد را دارید می‌توانید به مطلب «درک بسیار ساده async در جاوااسکریپت» مراجعه کنید. فارغ از آن برای مشاهده ویدیوهای آموزشی می‌توانید به دوره‌های «آموزش جاوااسکریپت ES۶» و «آموزش جاوااسکریپت ES۷ و ES۸» مراجعه نمایید.

نکته: در این مطلب قصد تعریف کامل Callback و Promiseها را نداریم چرا که برای اینکار می‌توانید به لینک‌های بالا مراجعه کنید. در این قسمت تنها قصد داریم تا اشاره‌ای کوچک به آن‌ها بکنیم.

یک مثال از Callback

setTimeout(() => {
  console.log('This runs after 1000 milliseconds.');
}, 1000);

به عنوان یک مثال از Callback که در آن توابع به صورت تودرتو قرار خواهند گرفت می‌توان مورد بالا را در نظر گرفت.

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

درک برنامه‌نویسی Async/Await از طریق یک مثال عملی

این حالت را Callback Hell می‌نامند که برای دریافت اطلاعات بیشتر در ارتباط با این موضوع می‌توانید ویدیو «آشنایی با جهنم Callback‌ها» را مشاهده کنید.

یک مثال از Promiseها

const promiseFunction = new Promise((resolve, reject) => {
  const add = (a, b) => a + b;
  resolve(add(2, 2));
});

promiseFunction.then((response) => {
  console.log(response);
}).catch((error) => {
  console.log(error);
});

سینتکس Promiseها در اکمااسکریپت ۶ بسیار منحصر به فردتر از هر تکنیک دیگری عرضه شد،‌ به همین خاطر در نسخه بعدی اکمااسکریپت نیاز دیدند تا سینتکسی مشابه‌تر با موارد قبلی را ارائه کنند. .then و .catch کلمات کلیدی جدیدی هستند که همراه با Promise برای مدیریت اجرای آن ایجاد شدند.

توابع Async

تابع Async روشی بسیار ساده‌تر و البته تمیزتر برای رسیدن به همان خروجی‌هایی‌ است که از طریق Promise می‌توان به آن‌ها دست یافت. برای استفاده از قابلیت async تنها کافی‌ست تا کلمه async را در قبل از تعریف تابع به کار ببرید. 

const asyncFunction = async () => {
  // Code
}

توابع Async را می‌توان با استفاده از دستور await متوقف کرد. این کلمه کلیدی تنها در داخل توابع Async کاربر داشته و خارج از آن برای مفسر جاوااسکریپت بی معنی است. 

تفاوت میان Async و Promise را می‌توانید در کدهای زیر مشاهده کنید:

// Async/Await

const asyncGreeting = async () => 'Greetings';

// Promises

const promiseGreeting = () => new Promise(((resolve) => {

  resolve('Greetings');

}));

asyncGreeting().then(result => console.log(result));

promiseGreeting().then(result => console.log(result));

اگر به کدهای بالا نگاه کنید متوجه خواهید شد که ساختار کدهای مربوط به Async خواناتر و بیشتر به کدهای synchronous شبیه است. 

حال با درک موارد مقدماتی، بیایید با مثال واقعی که قصد حل کردن آن را داریم ادامه دهیم.

مبدل ارز

در این مطلب قصد داریم برای درک بهتر Async/Await یک مثال عملی را حل کنیم. این برنامه قرار است که یک واحد پولی را دریافت کرده و آن را به یک واحد دیگر تبدیل کند. برای انجام چنین کاری نیاز است که از یکسری API استفاده کنیم.

دو منبع asynchronous ما برای این مطلب عبارت هستند از:

  • Currency Layer – برای دسترسی به API این وبسایت نیاز است در آن ثبت نام کنید. این API به ما کمک می‌کند تا نرخ تبدیل بین ارزهای مختلف را محاسبه کنیم.
  • Rest Countries – این API به ما کمک می‌کند تا بدانیم ارزی که تبدیل شده در چه مکان‌هایی قابل استفاده است.

برای شروع ابتدا یک دایرکتوری جدید را ایجاد کرده و دستور npm init را در آن اجرا کنید. بعد از آن axios را با دستور npm I --save axios نصب نمایید. در نهایت نیز فایل currency-converter.js را ایجاد کنید. 

در ابتدای این فایل axios را با استفاده از یک دستور require وارد پروژه نمایید:

const axios = require(‘axios’);

نوشتن توابع Async/Await

هدف ما در این برنامه نوشتن سه تابع غیرهمزمان است: تابع اول برای دریافت اطلاعات در ارتباط با ارزهای مختلف، تابع دوم برای دریافت اطلاعات در ارتباط با کشورها و تابع سوم نیز برای جمع آوری اطلاعات در یک مکان و ایجاد یک خروجی زیبا برای کاربران.

تابع اول – دریافت اطلاعات ارزها به صورت غیرهمزمان

تابع اول که البته قرار است به صورت غیرهمزمان نیز ایجاد شود دو ورودی را دریافت می‌کند. ورودی اول ارز مبدا و ورودی دوم ارز مقصد. به کدهای زیر دقت کنید:

const getExchangeRate = async (fromCurrency, toCurrency) => {}

حال نیاز است تا داده‌های مورد نظرمان را از طریق API دریافت کنید. با استفاده از قابلیت await من می‌توانم داده‌های مورد نظرم را به صورت مستقیم در یک متغیر قرار دهم. 

const getExchangeRate = async (fromCurrency, toCurrency) => {
  const response = await axios.get('http://data.fixer.io/api/latest?    access_key=[yourAccessKey]&format=1');
}

نکته: در کدهای بالا من اطلاعات مربوط به دسترسی به API را وارد نکرده‌ام چرا که این داده‌ها برای هر فردی منحصر به فرد است پس مطمئن شوید که Access Key خودتان را در کدهای بالا وارد کرده‌اید.

داده‌های مورد نظر ما از طریق response.data.rates قابل دسترس است. می‌توانیم این بخش را نیز در یک متغیر دیگر قرار دهیم:

const rate = response.data.rates;

از آنجایی که همه چیز از یورو تبدیل می‌شود در زیر یک متغیر با نام euro ایجاد کرده‌ایم که برابر با 1/fromcurrency خواهد بود. دقت کنید که منظورمان از fromcurrency همان ارز مبدا است:

const euro = 1 / rate[fromCurrency];

در نهایت برای دریافت نرخ ارز مقصد می‌توانیم به صورت زیر عمل کنیم:

const exchangeRate = euro * rate[toCurrency];

در نهایت تابع شما باید شبیه به زیر باشد:

درک برنامه‌نویسی Async/Await از طریق یک مثال عملی

تابع دوم – دریافت اطلاعات مربوط به کشورها به صورت غیرهمزمان

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

const getCountries = async (currencyCode) => {}

همانطور که قبلا این موضوع را مشاهده کردیم، با استفاده از await یک مقداری را به متغیر response انتساب می‌کنم:

const response = await axios.get(`https://restcountries.eu/rest/v2/currency/${currencyCode}`);

حال از طریق country می‌توانیم داده‌های مختلف را پیمایش کرده و نام کشور مورد نظرمان را بدست بیاوریم:

return response.data.map(country => country.name);

تابع شما در نهایت باید شبیه به زیر باشد:

درک برنامه‌نویسی Async/Await از طریق یک مثال عملی

تابع سوم – ادغام تمام این موارد

در تابع سوم ما سه ورودی fromCurrency، toCurrency و amount را خواهیم داشت:

const convert = async (fromCurrency, toCurrency, amount) => {}

ابتدای کار باید داده‌های مربوط به Currency را دریافت کنیم:

const exchangeRate = await getExchangeRate(fromCurrency, toCurrency);

بعد از آن اطلاعات مربوط به Country:

const countries = await getCountries(toCurrency);

بعد از مورد اول و دوم باید مقدار تبدیل شده را در یک متغیر ذخیره کنیم:

const convertedAmount = (amount * exchangeRate).toFixed(2);

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

return `${amount} ${fromCurrency} is worth ${convertedAmount} ${toCurrency}. You can spend these in the following countries: ${countries}`;

در پایان تابع شما باید به صورت زیر باشد:

درک برنامه‌نویسی Async/Await از طریق یک مثال عملی

افزودن یک try/catch برای مدیریت خطاها

حال نیاز است که برای مدیریت بهتر اپلیکیشن try/catch بهینه‌ای را تعریف کنیم:

const getExchangeRate = async (fromCurrency, toCurrency) => {

  try {

    const response = await       axios.get('http://data.fixer.io/api/latest?access_key=f68b13604ac8e570a00f7d8fe7f25e1b&format=1');

    

    const rate = response.data.rates;

    const euro = 1 / rate[fromCurrency];

    const exchangeRate = euro * rate[toCurrency];

    

    return exchangeRate;

  } catch (error) {

    throw new Error(`Unable to get currency ${fromCurrency} and  ${toCurrency}`);

  }

};

برای تابع دوم نیز می‌توانید به همین صورت عمل کنید:

const getCountries = async (currencyCode) => {

  try {

    const response = await axios.get(`https://restcountries.eu/rest/v2/currency/${currencyCode}`);

    

return response.data.map(country => country.name);

  } catch (error) {

    throw new Error(`Unable to get countries that use ${currencyCode}`);

  }

};

از آنجایی که تابع سوم به صورت ادغام شده با دو مورد قبلی کار می‌کند نیازی به استفاده از try/catch در آن نیست. 

در پایان کار می‌توانیم به صورت زیر تابع ساخته شده را امتحان کنیم:

convertCurrency('USD', 'HRK', 20)

  .then((message) => {

    console.log(message);

  }).catch((error) => {

    console.log(error.message);

  });

خروجی که شما دریافت خواهید کرد به صورت زیر خواهد بود:

درک برنامه‌نویسی Async/Await از طریق یک مثال عملی

در پایان

در این مطلب از وبسایت راکت ابتدا با حالت‌های مختلف برنامه‌نویسی asynchronous آشنا شدیم و بعد از آن با یک مثال واقعی در سه تابع مختلف کار با Async/Await را یاد گرفتیم. برای یادگیری موارد بیشتر در ارتباط با این موارد می‌توانید به دوره‌های آموزشی «آموزش جاوااسکریپت ES۶» و «آموزش جاوااسکریپت ES۷ و ES۸» مراجعه کنید.

منبع

گردآوری و تالیف ارسطو عباسی
آفلاین
user-avatar

من ارسطو‌ام :) کافی نیست؟! :)

دیدگاه‌ها و پرسش‌ها

برای ارسال نظر لازم است ابتدا وارد سایت شوید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید