🎁 تخفیف ویژه تمامی دوره های آموزشی 🎁

آموزش async/await در جاوا اسکریپت

async/await جدیدترین روش نوشتن کدهای غیرهمزمان در زبان برنامه نویسی جاوااسکریپت است که برای ساده سازی روند کار و نوشتن promise های زنجیره ای طراحی شده است.

آموزش async/await در جاوا اسکریپت

async/await در جاوا اسکریپت

یک سینتکس ویژه برای کار با promises به شیوه ای راحت تر وجود دارد، به نام «async/wait» که درک و استفاده از آن به طرز شگفت انگیزی آسان است. کلمات کلیدی async و await در ES2018 برای کاهش تکرارها و حل محدودیت "در شکستن زنجیره" در promise ها معرفی شد. اما تفاوت بین promises و async/await چیست؟ هنگامی که از async/await استفاده می شود، then/catch به ندرت استفاده می شود. چرا که در درون خود مقادیر به صورت مستقیم به promise مورد نظر برمیگردند و دیگر نیازی به نوشتن زنجیره ای برای آنها نیست.

ویژگی های کلی async/await

1- جدیدترین روش نوشتن کدهای غیرهمزمان در زبان برنامه نویسی جاوااسکریپت است.

2- غیرقابل انسداد است مانند promise و callback

3- async/await برای ساده سازی روند کار و نوشتن promise های زنجیره ای طراحی شده است.

4- توابع async یک promise را برمیگردانند. اگر تابع یک خطا را برگرداند promise رد شده و اگر تابع یک مقدار را برگرداند promise پذیرفته شده است.

Async/Await یک ویژگی پیش بینی شده ی جاوااسکریپت است که کار با توابع ناهمگام را بسیار لذت بخش تر و قابل فهم تر کرده است. همانطور که گفته شد Async/Await برمبنای Promise ها ساخته شده است و با تمام API های موجود که پایه ی آن ها Promise هاست سازگار است.

نام Async/Await از async و await تشکیل شده است این دو کلمه ی کلیدی به ما کمک می کند تا کد های ناهمگام را مرتب کنیم.

async/await in javascript

توابع ناهمگام (async functions)

توابع async توسط ES8 به جاوااسکریپت اضافه شده و برای راحت تر کردن مدیریت عملیات ناهمگام استفاده می شود. توابع async به طور نهفته از پرامیس ها استفاده می کنند. برای درک این که این توابع واقعا چه هستند و چگونه کار میکنند ابتدا باید promise ها را به خوبی درک کنیم که در مقاله قبلی آموزش زبان برنامه نویسی جاوا اسکریپت با موسسه آموزشی آتریا کامل توضیح داده شده است. نحوه استفاده از توابع async بسیار ساده است. برای استفاده از این ویژگی برای مدیریت یک عملیات ناهمگام، ابتدا از کلمه کلیدی async هنگام تعریف کردن یک تابع استفاده می کنیم. کلمه async رو همیشه ابتدای تعریف تابع می نویسیم، مانند زیر:

async functions in javascript

کلمه "async" قبل از یک تابع به معنای یک چیز ساده است: یک تابع همیشه یک promise را برمی گرداند. سایر مقادیر به صورت خودکار در یک promise حل شده قرار می گیرند.

به عنوان مثال، این تابع یک promise حل شده را با نتیجه 1 برمی گرداند. بیایید آن را آزمایش کنیم:

async functions

ما می توانیم صراحتاً یک promise را برگردانیم که همان خواهد بود:

async functions

بنابراین، async تضمین می کند که تابع یک promise را برمی گرداند، و غیر promise ها را در آن قرار می دهد. بسیار ساده، درست است؟ اما کلمه کلیدی دیگری وجود دارد، await، که فقط در داخل توابع async کار می کند، و بسیار جالب است.

await

await اجرای توایع async را متوقف می کند. زمانی که await بعد از یک فراخوانی promise قرار می گیرد بقیه ی کد هارا منتظر نگه می دارد تا promise تمام شود و یک مقدار برگرداند. توجه داشته باشید که await فقط با promise ها کار میکند نه با callback ها. همچنین await فقط می تواند داخل توابع async استفاده شود.

سینتکس await به صورت زیر است:

await in javascript

همانطور که گفته شد کلمه کلیدی await باعث می شود جاوا اسکریپت منتظر بماند تا این promise حل شود و نتیجه خود را برگرداند.

در اینجا یک مثال با یک promise نشان داده شده است که در 1 ثانیه حل می شود:

await in javascript

اجرای تابع در خط (*) "مکث" می کند و زمانی که promise حل شد از سر گرفته می شود و نتیجه به نتیجه آن تبدیل می شود. بنابراین کد بالا "done!" را در یک ثانیه نشان می دهد.

بیایید تاکید کنیم: await به معنای واقعی کلمه اجرای تابع را تا زمانی که promise حل شود به حالت تعلیق در می آورد و سپس با نتیجه promise آن را از سر می گیرد. این عمل هیچ هزینه ای برای منابع CPU ندارد، زیرا جاوا اسکریپت می تواند کارهای دیگری را در این بین انجام دهد: اجرای اسکریپت های دیگر، مدیریت event ها و غیره.

این فقط یک سینتکس زیباتر برای دریافت نتیجه promise نسبت به promise است. همچنین، خواندن و نوشتن آسان تر است.

نمی توان از await در توابع معمولی (regular functions) استفاده کرد.

اگر بخواهیم از await در یک تابع غیر همگام (non-async function) استفاده کنیم، یک خطای نحوی (syntax error) وجود خواهد داشت:

non-async function in javascript

اگر فراموش کنیم که کلمه کلیدی async را قبل از یک تابع قرار دهیم، ممکن است این خطا را دریافت کنیم. همانطور که قبلا گفته شد، await فقط در داخل یک تابع همگام (async function) کار می کند.

مثال زیر را ببینید.

async function in javascript

مرورگرهای مدرن به await سطح بالا (top-level await ) در ماژول ها اجازه می دهند.

در مرورگرهای مدرن، زمانی که ما در یک ماژول هستیم، await در سطح بالا به خوبی کار می کند. برای مثال:

async/await in javascript

اگر از ماژول ها استفاده نمی کنیم، یا مرورگرهای قدیمی باید پشتیبانی شوند، یک دستور العمل جهانی وجود دارد: قرار دادن در یک تابع ناهمگام بی نام (anonymous async function)

مثل این:

anonymous async function

Await ، “thenables” را می پذیرد.

مانند promise.then،await به ما اجازه می دهد تا از اشیاء thenable (آنهایی که متد then قابل فراخوانی دارند) استفاده کنیم. ایده این است که یک شی third-party ممکن است یک promise نباشد، اما با promise سازگار باشد: برای استفاده از آن با await کافی است که از .then پشتیبانی کند.

در اینجا یک کلاس Thenable دمو وجود دارد. await زیر نمونه های خود را می پذیرد:

await/async in javascript

اگر await یک شی non-promise با .then دریافت کند، آن متد را فراخوانی می کند که توابع built-in را به عنوان آرگومان ها حل و رد می کند (همانطور که برای یک اجراکننده Promise معمولی انجام می شود). سپس await منتظر می ماند تا یکی از آنها فراخوانی شود (در مثال بالا در خط (*) اتفاق می افتد) و سپس با نتیجه ادامه می یابد.

متدهای کلاس Async

برای اعلان یک متد کلاس async، کافی است async را به آن اضافه کنید:

متدهای کلاس Async

معنی و مفهوم یکسان است: تضمین می کند که مقدار بازگشتی یک promise است و await را فعال می کند.

کنترل خطا

اگر یک promise به طور معمولی حل شود، پس از آن await promise، نتیجه را برمی گرداند. اما در حالت rejection، خطا می دهد، درست مثل اینکه یک عبارت پرتاب در آن خط وجود داشته باشد. کد زیر را ببینید.

کنترل خطا در جاوا اسکریپت

که با آنچه که در ادامه نشان داده شده است یکسان است:

آموزش زبان برنامه نویسی جاوا اسکریپت

در شرایط واقعی، promise ممکن است مدتی طول بکشد تا رد شود. در این صورت یک تاخیر قبل از ایجاد خطا توسط await وجود خواهد داشت.

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

آموزش await/async زبان برنامه نویسی جاوا اسکریپت

در صورت بروز خطا، کنترل به بلوک catch می پرد. ما همچنین می توانیم چندین خط را بپیچیم:

await/async

اگر try..catch نداشته باشیم، promise ایجاد شده با فراخوانی تابع async به نام ()f رد می شود. می توانیم catch. را برای مدیریت آن اضافه کنیم:

async/await

اگر فراموش کنیم catch. را در آنجا اضافه کنیم، خطای promise unhandled دریافت می کنیم (قابل مشاهده در کنسول). ما می توانیم چنین خطاهایی را با استفاده از کنترل کننده رویداد جهانی unhandledrejection کشف کنیم.

async/wait و promise.then/catch

وقتی از async/wait استفاده می کنیم، به ندرت به then. نیاز داریم، زیرا await انتظار را برای ما کنترل می کند. و می توانیم به جای catch. از یک try..catch معمولی استفاده کنیم. این معمولا (اما نه همیشه) راحت تر است.

اما در سطح بالای کد، وقتی خارج از هر تابع همگام سازی (async function) هستیم، از لحاظ سینتکسی نمی توانیم از await استفاده کنیم، بنابراین اضافه کردن then/catch. برای مدیریت نتیجه نهایی یا خطای falling-through، یک تمرین نرمال است. مانند آنچه که در خط (*) مثال بالا نشان داده شده است.

async/await با Promise.all به خوبی کار می کند.

هنگامی که باید منتظر چندین promise باشیم، می توانیم آنها را در Promise.all بپیچیم و سپس منتظر بمانیم:

async/wait و  promise.then/catch

در صورت بروز خطا، طبق معمول، از promise ناموفق به Promise.all منتشر می شود، و سپس تبدیل به یک استثنا می شود که می توانیم آن را با استفاده از try..catch در اطراف فراخوانی بگیریم.

خلاصه و چکیده مطالب async/await در زبان برنامه نویسی جاوا اسکریپت:

کلمه کلیدی async قبل از یک تابع دو اثر و نتیجه دارد:

1- باعث می شود که همیشه یک promise را برگرداند.

2- اجازه می دهد تا await در آن استفاده شود.

کلمه کلیدی await قبل از یک promise باعث می شود جاوا اسکریپت منتظر بماند تا آن promise حل شود و سپس:

1- اگر خطا باشد، یک استثنا ایجاد می شود - مثل اینکه خطای پرتاب در همان مکان فراخوانی شود.

2- در غیر این صورت، نتیجه را برمی گرداند.

آنها با هم یک چارچوب عالی برای نوشتن کدهای غیرهمزمان (asynchronous) ارائه می دهند که خواندن و نوشتن آن آسان است.

با async/await به ندرت نیاز به نوشتن promise.then/catch داریم، اما هنوز نباید فراموش کنیم که آنها بر پایه و اساس promise ها هستند، زیرا گاهی اوقات (مثلاً در بیرونی ترین محدوده) ما باید از این متدها استفاده کنیم. همچنین Promise.all زمانی خوب است که ما به طور همزمان منتظر چندین تسک هستیم.

در ادامه چند تمرین به همراه پاسخ انها جهت فهم بیشتر نشان شده است.

بازنویسی با استفاده از async/wait

کد زیر را با استفاده از async/await به جای then/catch. بازنویسی کنید:

بازنویسی با استفاده از async/wait

نکات مهم در زیر کد آمده است:

بازنویسی با استفاده از async/wait

نکات مهم:

1- تابع loadJson غیر همگام (async) می شود.

2- همه then. ها با await جایگزین می شوند.

3- ما می توانیم به جای اینکه منتظر ()answer.json باشیم، آن را برگردانیم، مانند زیر:

بازنویسی با استفاده از async/wait

سپس کد بیرونی باید منتظر باشد تا آن promise حل شود. در case ما این مهم نیست.

4- خطای پرتاب شده از loadJson از طریق catch. کنترل می شود. ما نمی توانیم از (…)await loadJson در آنجا استفاده کنیم، زیرا در یک تابع async نیستیم.

فراخوانی غیرهمگام از غیر همگام (async from non-async)

ما یک تابع "regular" به نام f داریم. چگونه می توان تابع async به نام ()wait را فراخوانی کرد و از نتیجه آن در داخل f استفاده کرد؟

async from non-async

P.S. این کار از نظر فنی بسیار ساده است، اما این سوال برای توسعه دهندگانی که تازه با async/wait آشنا شده اند بسیار رایج است.

راه حل

فراخوانی async را به عنوان یک promise در نظر بگیرید و سپس به آن then. وصل کنید:

async from non-async
Async Await

مقالات این دسته بندی