ویرگول
ورودثبت نام
بیژن حجازی
بیژن حجازیسلام! نوشتن به منزله مطالعه «خود» هست. من بیشتر تو زمینه برنامه نویسی و روان شناسی مطلب میذارم. خوشحال میشم نظرات تون رو باهام در میون بذارین.
بیژن حجازی
بیژن حجازی
خواندن ۸ دقیقه·۳ سال پیش

کلیدواژه async در JavaScript

کلیدواژه async یک تابع رو مجبور میکنه که Promise‌ برگردونه.
کلیدواژه async یک تابع رو مجبور میکنه که Promise‌ برگردونه.

مقدمه

برای درک عمیق کلیدواژه async در JavaScript باید مسیر نسبتا طولانی ای رو طی کنیم. داستان از تفاوت بین کد های Synchronous و Asynchronous شروع میشه. بعدش باید در باره Promise ها مطالعه کنیم و در انتهای داستان متوجه میشیم که async در حقیقت یک میانبر برای کار کردن با Promise هاست. به این میانبر ها میگن Syntactic sugar. یعنی در پشت صحنه چیزی فرق نمیکنه ولی نوشتن کد برای برنامه نویس راحت تر میشه.

کد synchronous و asynchronous

ترجمه واژه synchronous میشه «همزمان». همون طوری که میدونیم زبان های برنامه نویسی خط به خط خونده میشن و در نهایت کل کد در لحظه و در یک زمان اجرا میشه. به این میگن کد synchronous. در مقایسه با کد همزمان، کد غیر همزمان یا همون asynchronous رو داریم. این کد هر چند خط به خط خونده میشه ولی خط به خط اجرا نمیشه. بذار با یک مثال توضیح بدم. در کد زیر یک کد همزمان رو مشاهده میکنید که خط به خط، به ترتیب از بالا به پایین خونده و اجرا میشه:

let a = 1; let b = 2; console.log(a); // 1 console.log(b) // 2

کد بالا خیلی ساده بود. حالا کد پایین رو نگاه کن. در کد پایین که به صورت asynchronous نوشته شده هر چند برنامه به صورت خط به خط خونده میشه، ولی به صورت خط به خط اجرا نمیشه. بخشی از برنامه در یک زمان اجرا میشه و بخش دیگری که asynchronous هست در زمان دیگری اجرا میشه.

let a = 1; let b = 2; setTimout(()=>console.log(a), 1000); console.log(a); a = 10; console.log(a);

قسمتی از کد که bold کردیم asynchronous هست چون ۱۰۰۰ میلی ثانیه بعد از این که بقیه قسمت های کد اجرا شدن، اجرا میشه. به همین دلیل غیر همزمان بودن اجرای این قسمت از کد در مقایسه با بقیه قسمت های کد میگن asynchronous بودن. حالا به نظر شما بعد از ۱۰۰۰ میلی ثانیه خروجی خط bold شده چه خواهد بود؟! ۱ یا ۱۰؟

شاید باعث تعجب بشه ولی خروجی ۱۰ خواهد بود! چرا که قسمت a = 10 زودتر از خط bold شده اجرا میشه و مقدار a رو عوض میکنه. زمانی که بعد از ۱۰۰۰ میلی ثانیه خط bold شده میخاد اجرا بشه دیگه مقدار متغیر a تغییر کرده. به همین دلیله که کد نویسی به صورت asynchronous چالش های بیشتری داره. باید حواسمون باشه با توجه به این تاخیر در اجرا شدن کد asynchronous متغیر ها مقدارشون عوض نشه.

مفهوم Promise

خوب حالا که با مفهوم asynchronous آشنا شدیم می تونیم Promise ها رو درک کنیم. واژه promise در انگلیسی یعنی «قول». در JavaScript هم promise «قول» ای هست که داده میشه. بذار با یک مثال توضیح بدم:

فرض کن میری رستوران و غذا سفارش میدی. آماده شدن غذا طول میکشه. رستوران بهت یک دونه buzzer (از این دایره هایی که وقتی غذات آماده میشه بوق میزنه) میده و میگه فعلا این دست شما باشه تا ما داریم غذا رو آماده میکنیم. این جا رستوران به جای این که غذای اصلی رو به شما بده یک buzzer به شما داده که به عنوان «قول» هست. شما هم پای میز پیشخوان وای نمیستین تا غذا آماده بشه و دریافت کنین! میرین با buzzer پشت میز میشینین بقیه کاراتون رو انجام میدین تا buzzer بوق بزنه.

به این ها میگن buzzer
به این ها میگن buzzer

توی برنامه نویسی هم promise همین طوری عمل میکنه. فرض کن میخای از سمت سرور یک سری اطلاعات دریافت کنی. دریافت اطلاعات طول میکشه. به همین دلیل یک فرآیند asynchronous عه. JavaScript نمیاد و بگه خوب پس من صبر میکنم تا اطلاعات از سمت سرور دریافت بشه و بعدش میرم بقیه کد رو اجرا میکنم. اومدیم و اجرای کد asynchronous تا شب طول کشید! به جای این کار یک promise دریافت میکنه و میره بقیه قسمت های کد رو بدون درنگ اجرا میکنه.

در مثال بالا دریافت اطلاعات از سمت سرور همون دریافت غذا از رستوران، promise همون buzzer ای هست که رستوران به ما میده و JavaScript هم خود شما هستین که صبر نمیکنین پای پیشخوان رستوران تا غذا آماده بشه بلکه میرین سر میز میشینین و بقیه کاراتون رو انجام میدیم تا اون buzzer خودش بوق بزنه.

کد نویسی Promise ها

از نظر کد نویسی Promise ها object هایی هستند که با استفاده از تابع سازنده Promise درست شده و یک سری method و property روی خودشون دارن (مثل هر object دیگری). در صورتی که نتیجه نهایی (مثلا دریافت اطلاعات از سمت سرور) به صورت موفقیت آمیز حاصل بشه یک callback رو اجرا میکنن و در صورتی که هر مشکلی وسط داستان پیش بیاد هم یک callback دیگه ای رو صدا میکنن. این callback ها که به ترتیب resolve و reject نام دارن به صورت پیش فرص توسط خود JavaScript تعریف شدن و نیازی نیست به صورت دستی تعریف شون کنیم.

const promise = new Promise((resolve, reject) => { resolve(); // if successful reject(); // if error });

وضعیت Promise ها

بذار برگردیم به مثال مون. اون buzzer که از رستوران گرفته بودی رو یادته؟! اون buzzer سه تا وضعیت می تونه داشته باشه:

  • زمانی که میگیریش و منتظری که غذات آماده بشه.
  • زمانی که غذات آماده میشه و چراغ میزنه.
  • زمانی که به هر دلیلی درست کردن سفارشت به مشکل بر می خوره (مثلا رستوران کاهو تموم کرده !) و میان ازت معذرت خواهی میکنن و buzzer رو پس میگیرن!

توی JavaScript هم Promise ها سه تا وضعیت (state) مشابه دارن:

  • در حال کار (pending): یعنی promise هنوز داره صبر میکنه تا حاصل نهایی به دست بیاد.
  • انجام شده (fulfilled): حاصل نهایی حاضر شده و promise ازش آگاه هست. حالا میتونیم با حاصل نهایی هر کاری دوست داریم بکنیم. مثلا پرینتش کنیم.
  • پس زده شده (rejected): به هر دلیلی حاصل نهایی نتونسته حاضر بشه و promise دستش تو پوست گردو مونده. حالا باید با این وضعیت ناگوار دست و پنجه نرم کنیم.

متد های then و catch

دسترسی مستقیم به وضعیت promise ها ممکن نیست. به جاش یک سری متد روی آبجکت promise هست که ما می تونیم باهاشون کار کنیم. مثلا تابعی در browser ها هست به نام fetch که از یک آدرس اینترنتی اطلاعاتی رو برای ما دریافت میکنه. این تابع به صورت پیش فرض یک promise بر می گردونه که متد های مد نظر ما روش هست.

یکی از این متد ها then هست که یک callback دریافت میکنه. این callback هر زمان که وضعیت promise به fulfilled عوض بشه صدا زده میشه. متد بعدی catch هست که اون هم یک callback دریافت میکنه. این callback زمانی صدا زده میشه که وضعیت promise به rejected عوض شده باشه. به کد زیر دقت کن:

fetch('https://api.dictionaryapi.dev/api/v2/entries/en/smash').then(response => { console.log(response); });

کد بالا از آدرسی که نوشته شده قراره که یک object دریافت کنه. تا زمانی که این object دریافت نشده باشه تابع fetch به ما یک promise بر می گردونه. promise ها متدی دارن به نام then که یک callback دریافت میکنه. این callback در صورتی اجرا میشه که promise به وضعیت fulfilled برسه. در کد بالا هم وقتی که پاسخ از سمت URL داده شده دریافت بشه arrow function ای که داخل متد then به عنوان callback دادیم اجرا میشه و response رو برای ما log میکنه. متد then ها در نهایت promise بر میگردونه. این یعنی دقیقا بعد از آوردن متد then می تونیم متد catch رو هم برای مدیریت error زنجیر وار بنویسیم:

fetch('https://api.dictionaryapi.dev/api/v2/entries/en/smash').then(response => { console.log(response); }).catch(error => { console.error(error); });

الگو کد نویسی بالا چیزی هست که هنگام کار کردن با کتاب خونه هایی مثل Axios دائما باهاش سر و کار داریم و خیلی راهگشاست.

کلیدواژه async

خوب حالا که این همه صحبت کردیم تازه رسیدیم به اصل داستان. کلیدواژه async یک میانبره. این کلیدواژه رو قبل از تعریف توابع میذارن تا به صورت خودکار اون تابع Promise برگردونه. اگر قرار بود بدون کلیدواژه async کار کنیم برای برگردوندن یک promise از یک تابع باید این طوری کد میزدیم:

function getData() { const promie = new Promise((resolve, reject) => { // any other code goes here }); return promise; }

ولی با استفاده از کلیدواژه async خیلی راحت این طوری کد میزنیم (واسه راحت تر شدن از کد های ساده استفاده کردم):

async function getData(url) { return `${url} is ready`; }

در کد بالا ظاهرا مقداری که توسط تابع return میشه یک string عه ولی کلیدواژه async باعث میشه مقدار return شده توسط تابع به صورت اتوماتیک یک promise باشه.

کلیدواژه await

این کلیدواژه باعث میشه که اجرای قسمتی از کد درون یک تابع که با کلیدواژه async اعلان شده تا حاصل شدن نتیجه نهایی promise متوقف بشه. این طوری می تونیم خیلی راحت تر کد های asynchronous رو مدیریت کنیم. فرض کن که داریم از سمت سرور یک سری اطلاعات دریافت میکنیم و قصد داریم که حاصل رو داخل HTML نمایش بدیم. فرض کن بدون استفاده از کلیدواژه await این طوری کد بزنیم:

async function changeHTML() { let promise = new Promise((resolve, reject) => { resolve(&quotAll OK.&quot); }); document.querySelector(&quot#demo&quot) = promise; }

در کد بالا همون طوری که قبلا هم گفتیم JavaScript نمیاد صبر کنه تا نتیجه promise مشخص بشه و بعدش خط bold شده رو اجرا کنه. پس وقتی که به خط bold شده میرسه هنوز نتیجه promise مشخص نیست. حالا که نتیجه promise مشخص نیست پس چی داخل demo# نمایش بدیم؟

باید راهی باشه که اجرا کد رو تا نهایی شدن نتیجه promise متوقف کنیم. این راهکار کلیدواژه await عه. کافیه خط bold شده کد بالا رو این طوری تغییر بدیم:

document.querySelector(&quot#demo&quot) = await promise;

کلیدواژه await باعث میشه که function های async برای نهایی شدن حاصل یک promise صبر کنن. این در عمل یعنی اجرا کد داخل async function متوقف میشه تا اون promise حاصل نهاییش مشخص بشه.

نتیجه گیری

جاوا اسکریپت زبان زنده ای هست که از زمان تولدش تا الان تغییرات زیادی کرده. کلیدواژه های async و await در سال 2017 معرفی شدن و کار کردن با promise ها رو در JavaScript خیلی راحت تر کردن. این طوری عملا همه چیز به پشت صحنه میره و توسط خود JavaScript مدیریت میشه. در نهایت از وقتی که برای مطالعه این مقاله گذاشتی خیلی متشکرم.

javascriptprogrammingjsweb development
۲
۴
بیژن حجازی
بیژن حجازی
سلام! نوشتن به منزله مطالعه «خود» هست. من بیشتر تو زمینه برنامه نویسی و روان شناسی مطلب میذارم. خوشحال میشم نظرات تون رو باهام در میون بذارین.
شاید از این پست‌ها خوشتان بیاید