Async در مقابل Sync

سلام توی این مقاله قراره بیشتر مفاهیم رو توضیح بدیم و خیلی کمتر دست به کد میشیم ولی هرجا که نیاز باشه کد هم یه مقدار گفتم.

اول ببینیم فرق (Non-Blocking)Asynchronous و (Blocking)Synchronous چیه ؟

برنامه نویسی همزمان یا همون Synchronous به این معنی هستش که کد هایی که مینویسیم خط به خط اجرا بشن، یعنی چی خط به خط ؟؟؟ یعنی هر خط کد منتظر میمونه تا خط قبلی انجام بشه

پس شاید براتون قابل حدس باشه، مشکلی که اینجا به وجود میاد اینه که در برنامه نویسی همزمان اگه یه خط کد ما خیلیییی طول بکشه موجب کند شدن و فاجعه به بار اومدن بشه و باعث انسداد یا block شدن کد های ما بشه

مثال زیر رو ببینید:
توی این مثال کد ها خط به خط اجرا میشن یعنی اول line 1 چاپ میشه بعد line 2 چاپ میشه و بعدش تابع ()myFunction اجرا میشه و در آخرم line 4

حالا اگه این تابع ما یه عملیات ریاضی سنگین داشته باشه و خیلی طول بکشه باعث انسداد و کند شدن برنامه ما میشه

console.log(&quotline 1&quot);
console.log(&quotline 2&quot);
myFunction();
console.log(&quotline 4&quot);

خب، بریم سراغ برنامه نویسی Asynchronous یا همون ناهمزمان ؟؟؟

توی این نوع برنامه نویسی برخلاف Synchronous کد های ما خط به خط اجرا نمیشن بلکه، این نوع کد ها در پس قضیه(توی background) run میشن و وقتی که تموم شدن execute میشن

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

بریم یه مثال بزنیم و قابلیت Async رو شبیه سازی کنیم تا بهتر متوجه اش بشیم:

const p = document.querySelector(&quot.p&quot);
setTimeout(() => {
        p.textContent = &quotcode time&quot
}, 5 * 1000);
document.body.style.backgroundColor = &quotred&quot

جاوااسکریپت به خط اول میرسه و اجراش میکنه و بعد میره جلوتر و میرسه به تابع setTimeout و خب ما بهش گفتیم بعد از 5 ثانیه اجرا بشه پس میره تو بک گراند تا کاراش تموم بشه و وقتی که تموم شد خروجی رو برگردونه و به ما نشون بده

در واقع جاوااسکریپت بهش میگه خب اقای setTimeout تو کارت طول میکشه، پس من میرم خط های بعدی رو اجرا میکنم و تو هر وقت که کارت تموم شد منو خبر کن تا خروجی رو به کاربر نمایش بدم

و بعدش میره خط آخر ور اجرا میکنه

پس چی شد ؟؟؟

پس قضیه ترتیب اجرای کد ها اینطوری اجرا میشه:

const p = document.querySelector(&quot.p&quot); 
document.body.style.backgroundColor = &quotred&quot
setTimeout(() => { 
       p.textContent = &quotcode time&quot
 }, 5 * 1000); 

اول تگ p رو میگیره و بعدش بکگراند رو قرمز میکنه و در آخرم بعد از 5 ثانیه عبارت code time رو توی تگ p میزاره

حالا اصلا این Asynchronous به چه درد میخوره؟ خب همون خط به خط کد هامون اجرا بشن دیگه، مشکلش چیه ؟؟؟ با یک مثال ساده توضیحش میدم که مشکل از کجا آب میخوره

فرض کنید وارد یک سایت شدید و اون سایت میخواد یک عکس رو به همراه محتواش به شما نمایش بده و کل کد های js ما هم به صورت sync هستش

خب درخواست ما میره سمت سرور که عکس و متن هارو نمایش بده ولی چون اینترنت ما خیلی ضعیفه خیلی طول میکشه که عکس بخواد load بشه و چون کد های ما sync هستن تا وقتی عکس لود نشه مطالب بعد نمایش داده نمیشن یعنی به خاطر یک عکس کل کد های ما مسدود شده و اجرا نمیشن و چیزی به کاربر نمایش داده نمیشه

اما اگه اون کد قسمت ارسال درخواست برای لود کردن عکس ما به صورت Async باشه چی؟؟ مشکل ما حل میشه😉

در واقع میگه تو فعلا بقیه اطلاعات رو به کاربر نمایش بده وقتی عکس load شد اونم برات نمایش میدم. این load عکس حکم همون setTimeout رو داره


اما بریم عمیق تر بشیم ببینیم چطور این کد های ما پس قضیه جاوااسکریپت اجرا میشن ؟؟🤔

اصلا مگه نمیگن جاوااسکریپت تک نخی (single thread) هست پس چطور کد های ما non-blocking داره اجرا میشه ؟؟ 🤔

همون طور که توی سری مقاله های اندر احوالات جاوااسکریپت(کل قسمت هاش توی پروفایلم هست) توضیح دادم، Engine جاوااسکریپت شامل بخش هایی میشه که خلاصه اش رو به همراه توضیحات مختصرش توی عکس زیر میتونید ببینید:

توی مقاله های قبل(قسمت اول) راجع به همه اجزای عکس بالا مفصلا توضیح دادم

اما راجع به Callback Queue توضیح خاصی ندادیم اما الان میخواییم بریم بشکافیمش🔪

کد توی عکس بالا رو ببینید، ابتدا js خط اول و دوم رو وارد call stack میکنه اجرا میشن و میرن بعد میرسه به خط سوم(eventListener) و هفتم(fetch) و چون این دوتا عملیات async هستن و مربوط به web api هستن js به call stack میگه: آقای call stack این دوتا قطعه کد async هستن پس توی بکگراند انجامشون میدم و وقتی تموم شدن، نتیجه رو میفرستم برات، حالا یا این نتیجه با موفقیت انجام شده(resolve) یا شکست خورده(rejected)

اینجاست که Callback Queue به همراه event loop به میدون میاد، همون طور که از اسم callback queue پیداس، یک صفی از callback ها هستش که به ترتیب اولویت ورودشون، خارج میشن

توی Callback Queue کالبک هایی هستن که قرار بود جی اس پس قضیه هندلشون کنه

حالا event loop مرتب به callstack نگاه میکنه ببینه خالی هست یا نه، اگه خالی باشه سریعا یه دونه کالبک(طبق اولویت) از Callback Queue میگیره و میفرسته توش تا execute بشه (نه run)کجاکد ها run میشن؟عکس بالا رو ببین،توی web api


نکته: کلا کار هایی مثل ارسال درخواست ها(request)، ajax، DOM، تایمر ها و ... به صورت async توی web api انجام میشه در نتیجه این ها جز خود js نیستن بلکه جز web api مرورگر هستن
نکته: توی js چیزی به صورت Async اجرا نمیشه، اصلا js درکی از time و اینا نداره که(چرا؟) چون توی Engine جاوااسکریپت اعمال Async رخ نمیده بلکه خود runtime و callback queue و سایر موارد کار های async رو انجام میدن، اصلا non-blocking علتش همین اجزایی هست که نام بردیم
نکته: فکر نکنید هر وقت اسم callback اومد به صورت async اجرا میشه ها، مثلا کالبک توی تابع map به صورت async نیستش که
نکته: برای نوشتن کد async باید قبل اسم تابع کلمه async رو بزاریم و داخل تابع از await استفاده کنیم، توی این مقاله راجع به نوشتن کد حرف نمیزنم بلکه اینکه چطور کار میکنه بحث میکنیم ولی گفتم یه hint این وسط بدم

خب یه خلاصه بگیم ببینیم چه اتفاقاتی تا اینجا افتاد:

وقتی کل کد اجرا میشه،js میره سراغ load کردن img و fetch کردن داده ها و وقتی load عکس تموم شد وارد callback queue میشه و event loop هم میفرستتش به call stack

اما قضیه fetch چی میشه؟؟ایا اونم مستقیما کارش که تموم شد میره توی callback Queue ؟؟ نه زهی خیال باطل. عه !!! چرا؟ مگه عملیات async نیست پس چرا نمیره توی صف ؟؟

چون fetch یه پرامیس (آخر مقاله توضیح کوتاهی دادم که پرامیس چیه) بر میگردونه و پرامیس هام چون قلدر ترن صف جداگونه خودشون رو دارن که از قضا به صف callback Queue تقدم داره، اسم این صف که مخصوصا برای پرامیس ها ساخته شده Microtasks Queue هستش و fetch قصه ما میره توی اون و زود تر از load img انجام میشه

به طور خلاصه ترتیب اولویت اجرا توسط event loop اینطوری هست:

Sync code -> Microtask Queue -> Callback Queue

پس event loop همیشه اول microtask queue رو چک میکنه بعد callback queue

بیا تا بهث اثبات کنم، کد های زیر رو ببین:

console.log(&quotline 1&quot);

setTimeout(() => {
        console.log(&quotline 2&quot);
}, 0);

Promise.resolve(&quotline 3&quot).then((res) => console.log(res));

console.log(&quotline 4&quot);

به نظرت خروجی چیه؟؟؟

اول کد های sync اجرا میشن یعنی خط اول و آخر و بعدش پرامیس و بعدش هم تابع setTimeout

پس خروجی میشه

line 1
line 4
line 3
line 2


اینکه پرامیس ها چی هستن و چطور کار میکنن خیلی میشه مفصلا راجع بهش بحث کرد ولی خیلی خلاصه بگم پرامیس ها ابجکت هایی هستن که قراره در آینده یه مقداری رو تحویل ما بدن، حالا این مقدار میتونه موفقیت آمیز باشه یا غیر موفقیت آمیز که در اصطلاح به ترتیب بهشون میگن resolve و reject

اینکه چطور کار میکنن و چطور مینویسمشون رو دیگه توی این مقاله نمیشه گفت چون خیلی زیاد میشه


امیدوارم به دردتون خورده باشه❤️ یا حق