Async در جاوا اسکریپت چیست؟ – به زبان ساده + نمونه کد
سرآشپزی را تصور کنیم که در رستورانی بزرگ مشغول به کار است. در این رستوران همزمان، سرآشپز با دقت غذایی را در زمانی معین آماده میکند و منتظر میماند تا پخت این غذا تمام شود و سپس به دنبال پختن غذای بعدی میرود. این روش، روشی کند است و امکان دارد مشتریان از این رستوران رضایت نداشته باشند. این مسئله را میتوان به async در جاوا اسکریپت یا ناهمزمانی تعمیم داد. در آشپزخانه ستورانی ناهمزمان سرآشپز تبدیل به استادی چندوظیفهای میشود که چندین غذا را به طور همزمان سرو میکند و به طور دورهای پیشرفت پختوپز آنها را بررسی و تنظیم میکند.
این رویکرد تضمین میکند که سفارشات به سرعت انجام میشوند و اطمینان حاصل خواهد کرد که مشتریان غذای خود را در سریعترین زمان ممکن دریافت کنند. به طور مشابه، در حوزه برنامه نویسی، تکنیکهای ناهمزمان نرمافزار را قادر میسازد تا چندین کار را همزمان انجام دهد و منجر به اجرای سریعتر و کارآمدتر شود. جاوا اسکریپت، در هسته خود، «تکرشتهای» (Single Thread) است، به این معنی که خط خاصی از کد را در زمانی معین پردازش میکند. این طراحی میتواند مزایای عملکردی را با بهینهسازی استفاده از منابع ارائه دهد اما همچنین میتواند در هنگام مقابله با وظایف پیچیدهای که اجرای موازی را میطلبد، به نوعی محدودیت تبدیل شود. در این مطلب از «مجله فرادرس» async در جاوا اسکریپت را مورد بررسی قرار داده و به جنبههای ناهمزمانی جاوا اسکریپت خواهیم پرداخت.
async در جاوا اسکریپت چیست؟
جاوا اسکریپت ناهمزمان (ناهمگام | Asynchronous) به قابلیت جاوا اسکریپت برای ادامه عملیات خود بدون انتظار برای اتمام کار خاصی مربوط میشود. این ویژگی در زبان برنامه نویسی جاوا اسکریپت اجازه میدهد تا کدهای دیگر در حالی که در انتظار تکمیل کار ذکر شده است، اجرا شوند. توجه به این نکته مهم است که جاوا اسکریپت تکرشتهای است. این بدان معنی بوده که وظایف ناهمزمان به وسیله صف برگشتی و حلقه رویداد مدیریت میشوند.
در پارادایم جاوا اسکریپت «همزمان» (همگام | Synchronous)، توابع به صورت متوالی و یکی پس از دیگری اجرا میشوند و هر کدام منتظر پایان عملیات قبلی هستند تا به عملیات بعدی بروند. در این حالت، کد از بالا به پایین به صورت خطی جریان مییابد.
ماهیت برنامه نویسی async در جاوا اسکریپت
همانطور که قبلاً توضیح داده شد، جاوا اسکریپت، در هسته خود، به عنوان نوعی زبان تکرشتهای با زمینه اجرای جهانی عمل میکند. این نوع طراحی جاوا اسکریپت را ذاتاً همزمان کرده و از «پشته» (Stack) فراخوانی برای اجرای کد به شیوهای «آخرین ورودی، اولین خروجی» (LIFO) استفاده میکند. به عبارت سادهتر، async در جاوا اسکریپت به ترتیبی از اجرای متوالی پایبند است که در آن توابع یکی پس از دیگری، به ترتیبی که در آن فراخوانی میشوند پردازش خواهند شد. برای درک مفهوم جاوا اسکریپت همزمان، قطعه کد زیر را در نظر بگیرید:
1// Synchronous JavaScript
2console.log("synchronous.");
3console.log("synchronous javascript!");
4console.log("synchronous again!");
هنگامی که این کد اجرا می شود، خروجی به صورت زیر است:
// Output "synchronous" "synchronous javascript!" "synchronous again!"
مثال فوق به وضوح نشان میدهد که جاوا اسکریپت همزمان به صورت خطی پیشرفت کرده و برنامه کد را به ترتیب دقیق نوشته شده اجرا میکند.
چرا برنامه نویسی async در جاوا اسکریپت نیاز است؟
برنامه نویسی async در جاوا اسکریپت بسیار ضروری است زیرا چندین فرآیند را قادر میسازد تا بدون ایجاد اختلال در رشته اصلی به طور همزمان اجرا شوند. این مسئله بسیار مهم است زیرا رشته اصلی به عنوان ساختار دادهای که دنباله فعلی فراخوانیهای تابع را حفظ میکند، مسئول مدیریت پشته تماس است. هنگامیکه رشته اصلی مسدود میشود، منجر به کاهش عملکرد خواهد شد. از سوی دیگر، برنامهنویسی ناهمزمان تضمین میکند که رشته اصلی بازمیماند و اجازه میدهد تا در حین انجام عملیات ناهمزمان، کارهای اضافی اجرا شوند.
نمونهای از برنامه نویسی همزمان در جاوا اسکریپت را میتوان در تابع setTimeout()یافت. این تابع اجرای کد جاوا اسکریپت را پس از مدت زمان مشخصی فعال میکند. پس از سپری شدن بازه زمانی تعیین شده، کد را یک بار اجرا میکند. برای استفاده از قدرت تابع setTimeout()در جاوا اسکریپت، از توابع پیکان معرفی شده در «جاوا اسکریپت ES6» استفاده خواهد شد. این فراخوانی تابع دو پارامتر حیاتی دارد:
- تابع: تابعی که یک بلوک از کد را کپسوله میکند.
- میلیثانیه: تأخیر زمانی قبل از اجرای تابع است.
برای درک بهتر این فرایند قطعه کد زیر مد نظر است:
1console.log("asynchronous.");
2setTimeout(() => console.log("asynchronous javascript!"), 3000);
3console.log("asynchronous again!");
برخلاف مثال قبلی ، کد بالا به صورت همزمان در موتور جاوا اسکریپت اجرا نمیشود و این بار خروجی به صورت زیر خواهد بود:
1// asynchronous.
2// asynchronous again!
3// asynchronous javascript!
در اینجا تفکیک قطعه کد بالا به صورت زیر آمد است:
- مرحله ۱: خط اول بلافاصله اجرا و asynchronousدر کنسول ثبت میشود.
- مرحله ۲: متد setTimeout()فراخوانی میشود که تابع ناشناس را پس از ۳ ثانیه تأخیر (۳۰۰۰ میلیثانیه) برای اجرا برنامهریزی میکند. تابع ناشناس setTimeout()به کنسول وارد خواهد شد.
- مرحله ۳: خط سوم اجرا میشود و synchronous againبه کنسول وارد خواهد شد.
- مرحله ۴: پس از ۳ ثانیه، تابع ناشناس برنامهریزی شده قبلی از متد setTimeout()اجرا میشود و asynchronous javascriptرا ثبت میکند.
در اصل جاوا اسکریپت ناهمزمان قبل از ادامه اجرای توابع بعدی منتظر پاسخ نمیماند. این رفتار برای برنامههایی که به قدرت پردازش قابلتوجهی نیاز دارند بسیار سودمند است و باعث میشود چندین کار به طور همزمان پیشرفت کنند.
تکنیک های برنامه نویسی جاوا اسکریپت ناهمزمان
جاوا اسکریپت تکنیکهای مختلفی را برای کار با عملیات ناهمزمان ارائه میدهد که به کاربر امکان خواهد داد جریان کد خود را به طور مؤثر مدیریت و کنترل کند.
رویکردهای برنامه نویسی ناهمزمان در جاوا اسکریپت به صورت زیر هستند:
- Callbacks: Callbacksکاربر را قادر میسازد تا کدهای ناهمزمان را به شیوهای ساختاریافتهتر و مشابه توابع همزمان بنویسد. آنها شامل انتقال توابع به عنوان آرگومان به توابع دیگر هستند که پس از تکمیل یک کار خاص اجرا میشوند.
- Promises : Promises در جاوا اسکریپت که به عنوان وعده در جاوا اسکریپت از آن یاد میشود، فرآیند نوشتن کد جاوا اسکریپت ناهمزمان را ساده میکند. آنها روشی منطقی برای رسیدگی به عملیاتی ارائه میدهند که باید پس از زمان از پیش تعریفشده یا زمانی که شرایط خاصی برآورده میشود، رخ دهد. Promisesنوعی رویکرد ساختاریافته برای مقابله با عملیات ناهمزمان ارائه میدهد.
- Async/Await: Async/await در زبان برنامه نویسی جاوا اسکریپت نوعی افزودنی نسبتاً جدید به جاوا اسکریپت است که به خوانایی و مختصرنویسی کدهای ناهمزمان کمک میکند. این ویژگی به کاربر امکان میدهد کدی بنویسد که پس از مدت زمان مشخص یا زمانی که شرایط خاص به روشی سادهتر و شهودی انجام میشود اجرا شود. Async/wait در جاوا اسکریپت بر اساس وعدهها استوار است و روشی مدرن و عالی برای مدیریت عملیات ناهمزمان ارائه میدهد.
مفهوم Callbacks در ناهمزمانی
توابع جاوا اسکریپت فوقالعاده متنوع هستند و میتوانند به عنوان آرگومان به توابع دیگر منتقل شوند. این ویژگی منحصربهفرد امکان ایجاد «توابع برگشت تماس» (Callbacks) را فراهم میکند که توابعی هستند که پس از تکمیل تابعی بیرونی اجرا میشوند. فراخوانها به عنوان ورودی برای عملکردهای دیگر ارائه شده و فراخوانی تابعی به وسیله دیگری را تسهیل میکنند. مثال زیر این مفهوم را بیان میکند:
1function incrementDigits(callback) {
2 callback();
3}
در قطعه کد فوق تابع incrementDigits()تابع دیگری را به عنوان پارامتر میپذیرد و متعاقباً آن را فراخوانی میکند. در جاوا اسکریپت، این عمل معمولاً به عنوان استفاده از تابع پاسخ به تماس شناخته میشود. تابعی که به عنوان آرگومان به تابع دیگری ارسال میشود و در اصل نوعی تابع فراخوانی است. فراخوانها به طور گسترده در جاوا اسکریپت برای انجام اقدامات ناهمزمان مانند رسیدگی به درخواستهای «آجاکس» «AJAX» یا پاسخ دادن به تعاملات کاربر استفاده میشوند. با تکیهبر مفاهیم ارائه شده از قبل در مورد تابع setTimeout () فراخوانیها را میتوان به عنوان «توابع پیکان» (Arrow Functions) ES6، به عنوان سینتکسی مدرنتر از تابع جاوا اسکریپت نوشت:
1setTimeout(() => {
2 console.log("Output initiated after 5 seconds");
3}, 5000);
در مثال بالا، متدsetTimeout ()برای به تأخیر انداختن اجرای تابع استفاده میشود و رشته Output initiated after 5 secondsرا با استفاده از تابع پیکان به عنوان پاسخ به تماس، به کنسول ثبت میکند. تأخیر مشخص شده ۵۰۰۰ میلیثانیه یا ۵ ثانیه است. هنگامیکه عملیات ناهمزمان به پایان رسید، تابع Callbacksفراخوانی میشود و نتیجه را مدیریت میکند. در زیر مثال پیچیدهتری از این مفهوم آورده شده است:
1function incrementDigits(num, callback) {
2 setTimeout(function() {
3 num++;
4 console.log(num);
5 if (num < 10) {
6 incrementDigits(num, callback);
7 } else {
8 callback();
9 }
10 }, 1000);
11}
12
13incrementDigits(0, function() {
14 console.log('Done!');
15});
کد فوق نوعی تابع بازگشتی را نشان میدهد که تا زمانی که عدد به مقداری بیشتر از ۱۰ برسد، خود را فراخوانی میکند. در آن نقطه، اجرای تابع Callbacksارائه شده را آغاز خواهد کرد. همچنین تابعsetTimeout ()تأخیری را معرفی کرده و فرآیند طولانیتری را شبیهسازی میکند. این تابع به صورت تدریجی هر عدد را چاپ خواهد کرد. به این ترتیب که از ۰ شروع میشود و تا رسیدن به ۱۰ این عملیات ادامه مییابد و در آخر Doneرا نمایش میدهد. کد فوق به صورت ناهمزمان عمل میکند و به توابع اجازه میدهد تا به طور همزمان، بدون انتظار برای تکمیل یکدیگر اجرا شوند.
مکانیسم توابع ناهم زمان و فراخوانی
هنگامیکه با توابع ناهمزمان یا async در جاوا اسکریپت سروکار داریم، عملکرد این توابع را میتوان با بررسی مکانیسمهای اصلی آنها درک کرد. در زیر توضیحات کاملی از این توابع برای درک بهتر این موضوع ارائه شده است:
- «توابع و انتظارات ناهمگام» (Async Functions and Awaits): توابع ناهمزمان از کلمه کلیدی awaitاستفاده میکنند که به طور موقت اجرا را متوقف میکند تا زمانی که وعده مرتبط حل شود. این بدان معنی است که عملکرد اساساً تا زمانی که عملیات ناهمزمان با موفقیت کامل شود متوقف میشود. به عبارت دیگر، awaitبه کاربر این امکان را میدهد که توابع ناهمزمان را بهگونهای بنویسد که متوالی یا همزمان به نظر میرسد.
- «تماسهای ناهمزمان» (Asynchronous Callbacks): پاسخ به تماس ناهمزمان نوعی تابع مرتبه بالاتر محسوب میشود که برای اجرای عملکرد پاسخ به تماس ناهمزمان داخلی خود به صورت غیر مسدود کننده طراحی شده است. هنگامیکه تابع ناهمزمان تابع Callbacksرا فراخوانی میکند، در حالی که منتظر تکمیل تماس است، اجرای آن را متوقف نمیکند. در عوض، در حالی که تماس برگشتی مستقل عمل میکند، به عملیات خود ادامه میدهد. در شرایطی که توابع Callbacks تودرتو باشند معمولاً منجر به چیزی میشود که به آن callback hellیا جهنم تماس میگویند.
همچنین تکنیکهایی برای مدیریت و ساده کردن کد وجود دارد که در زیر به آنها اشاره شده است:
- «استفاده از Promise»: برای هر تابع Callbacks، میتوان نوعی وعده مربوطه ایجاد کرد. اگر عملیات برگشت تماس با موفقیت انجام شود، وعده به سرانجام میرسد یا بهاصطلاح حل میشود. اگر شکست بخورد، وعده مردود است. این روش به ساختار کد ناهمزمان و مدیریت مؤثرتر وظایف ناهمزمان کمک میکند.
- «اجرای Async/Await»: با استفاده از async/wait ، توابع ناهمزمان را میتوان به صورت متوالی هماهنگ کرد. اجرا در نقاطی که انتظار به کار میرود تا زمانی که وعده مرتبط با موفقیت به سرانجام برسد، متوقف میشود. این رویکرد خوانایی کد را افزایش میدهد و مدیریت عملیات ناهمزمان را با نشان دادن آنها بهگونهای که گویی به صورت متوالی اجرا میشوند، ساده میکند.