کال استک جاوااسکریپت چیست و کدها چگونه در پشت صحنه اجرا میشوند؟

کال استک (Call Stack ) جاوااسکریپت چیست ؟ کدها چگونه در پشت صحنه جاوااسکریپت اجرا میشوند ؟ در این مقاله میخواهم کمی از پشت صحنه این زبان شیرین باهاتون صحبت کنم و یخورده باهم بریم توی دل و روده این زبان جذاب ، پس با من همراه باشید :)به عنوان یک توسعه دهنده جاوااسکریپت که دیگه امروزه میشه هم سمت فرانت اند از اون استفاده کرد و هم بک اند و هم موبایل، دونستن اینکه این زبان در پشت صحنه چطور کار میکنه و کدهای شمارو به چه ترتیبی و به چه شکلی اجرا میکنه میتونه تاثیر بسزایی در عمق دانش برنامه نویسی شما بزاره و شمارو به مراتب به یک برنامه نویس بهتر تبدیل کنهاکوسیستم جاوااسکریپت پیچیده تر از قبل شده و به این روند پیچیده تر شدنش بازهم ادامه خواهد داد و داشتن دانشی عمیق از خود جاوااسکریپت میتونه کمک خیلی زیادی به بهتر درک کردن این زبان بکنه.هر توسعه دهنده جاوااسکریپتی قبل از شروع یادگیری و استفاده از هر Framework و Library بهتره درک مطلوبی از اینکه پشت صحنه چخبره و همه اینها چگونه انجام میشوند داشته باشه.شاید اسم v8 به گوشتون خورده باشه و شاید هم تابحال حتی اسمش را هم نشنیده باشید، اما اکثر افراد بطور کامل نمیدونن که این v8 کروم دقیقا چی هست و چطور کار میکنه و از چه اجزایی تشکیل شده و من هم از این قائده مستثنی نبودم و هیچ ایده ایی نداشتم که توی دل ماجرا چخبره و اصلا ران تایم یعنی چی و برام مثل یک دنیای ناشناخته و مرموز بود، تا اینکه زدم به دل ماجرا و امروز در این مطلب میخام لپ کلام و خلاصه اونچه که فهمیدم رو براتون توی چند کلمه بگم.خب حالا این call stack جاوااسکریپت چی هست ؟بریم سر اصل مطلب و بیش از این با مقدمه گویی سرتون رو درد نیارم، بگذارید اینطور شروع کنم که جاوااسکریپت single threaded language هست، به این معنا که فقط توانایی اجرای یک تسک و یا یک تکه کد را در لحظه داره و دارای یک call stack هست که در کنار سایر اجزا مثل Heap و Queue همگی باهم JS Model را تشکیل میدهند.توصیف بصری JS Modelمیدونم تا اینجای کار کمی گیج کننده و نامفهوم بنظر میرسه، اما تا انتهای این مقاله بطور کامل تک تک این اجزا را به زبانی ساده براتون توضیح میدم، پس بریم سراغ اولیش.call stackساختار داده ایی هست که فانکشن کال هارو ثبت میکنه، در واقع وقتی ما یک فانکشن را صدا میزنیم داریم یک تسک را به این قسمت ( stack ) اضافه میکنیم و اون تسک و یا اون تیکه کد را میفرستیم برای اجرا شدن. فانکشنی که ما اونرو صدا زدیم و در نتیجه اونرو به درون stack بردیم و منتظر اجرای کدهای درون اون هستیم، پس از return شدن از بالای stack خارج میشه، برای درک بهتر اینکه این قسمت چطور کار میکنه کد زیر رو ببینید:حال به تصویر زیر دقت کنید:نحوه و ترتیب اجرا شدن کد ها در call stack - خروجی درست 100 هست که در تصویر به اشتباه 28 نوشته شدهکد از بالا بصورت خطی خوانده میشه تا جایی که به این قسمت میرسه console.log(bar(6))فانکشن bar به عنوان تسک اول وارد call stack میشه و در return خودش فانکشن foo را صدا میزنه.و foo به عنوان تسک دوم وارد call stack میشه و عدد 100 را برمیگردونهحال یکی یکی تسک ها همونطور که در تصویر بالا میبینید از آخر call stack خارج میشوند و در نهایت عدد 100 در کنسول برای ما به نمایش در می آید. همه اینها بصورت تک به تک و خطی و تنها در چند میلی ثانیه انجام میشوند.نکته ایی که درمورد call stack وجود داره محدودیت اون هست، به عنوان مثال محدودیت مروگر گوگل کروم 16 هزار فریم هست و اگر شما داخل یک لوپ بی نهایت بیوفتی احتمال اینکه با ارور RangeError: Maximum call stack size exceeded یا اصطلاحا ( stack overflow ) روبرو بشید زیاده. کد زیر را ببینید:این فانکشن با سرعت زیادی خودش را پشت سرهم اجرا میکنه و اینکار تا جایی ادامه پیدا میکنه که ما به محدودیت 16 هزار استک فریم میرسیم و با اروری از سمت مرورگر که بصورت خودکار به این روند خاتمه میده مواجه میشویم.Heapیک منطقه بدون ساختار و تمام فضایی از مموری هست که به متغیر ها و آبجکت ها اختصاص داده میشود و آبجکت ها و متغیر های ما در این قسمت ذخیره میشوند.Queueران تایم جاوااسکریپت اصطلاحا شامل یک صف پیام هست، صفی از پیام های مرتبط با کالبک فانکشن های آماده پردازش. وقتی stack فضای کافی را دارا میباشد، یک پیام از صف خارج و پردازش میشود که آن پیام مرتبط با یک فانکشن و یا یک تکه کدی هست که باعث ایجاد یک stack frame میشود و پردازش پیام زمانی به اتمام میرسد که استک مجددا خالی میشود.میدونم که توضیح بالا گیج کننده و کمی نامفهوم بود، پس به زبان ساده تر این پیام ها در پاسخ به وقایع خارجی صف میشوند، وقایع خارجی مثل Browser Web APIs یا همان چیزهایی که مرورگر در اختیار ما قرار داده است. و async http request و ...... نمونه هایی از Browser Web APIs میباشند.نمونه کد async http requestکد بصورت خطی خوانده و اجرا میشود و به سرعت از متغیر response که هنوز جوابی برای آن از سمت سرور ارسال نشده عبور میکند و در نتیجه باید در کنسول برای ما undefiend چاپ شود و اجرای کد از این قسمت برای همیشه عبور کند. ولی این اتفاق نمی افته و به محض آمدن پاسخی از سمت سرور دیتای ما از طریق لاگی که گرفتیم برای ما به نمایش در می آید.کد بالا یعنی فانکشن fetchData یک پیام درحال پردازش در Queue هست و مرورگر به محض آمدن یک پاسخ برای این http request اونرو وارد call stack میکنه و در نتیجه دیتای ارسالی از سمت سرور در کنسول ما چاپ میشود.Event Loopتا به حال اجرای کدهای مختلف رو تست کردید ؟ که چه مدت زمانی میبره تا اجرا شوند، کند اجرا میشوند و یا سریع ؟به عنوان مثال یک console.log سریع اجرا میشود اما یک forEach روی یک آرایه با عضو های زیاد طبیعتا کندتر خواهد بود و همچنان که این تکه کد forEach ما مشغول اجرا و پردازش میباشد stack را بلوکه میکند و مانع اجرای فریم بعدی میشود که اصطلاحا آنرا blocking script مینامیم.یک http request گاهی وقتا میتونه بشدت کند باشه، فکرش را بکنید که همین http request بصورت sync اجرا شود، چه اتفاقی خواهد افتاد ؟ سروری که به احتمال خیلی زیاد در جا و مکان دیگری قرار داره رو ما یک درخواست به سمتش ارسال میکنیم، حال این سرور باید درخواست مارو پردازش کنه و بعد که پردازشش تمام شد یک پاسخی به سمت ما بفرسته و ما پاسخ را دریافت کنیم و مابقیه کدرا طبق اون پاسخ اجرا کنیم در حالت عادی که سرور و اینترنت ما از قدرت و سرعت خوبی برخوردار باشه هم قطعا زمان زیادی خواهد برد چه برسه به اینکه برای لحظه ایی اون سرور کند عمل کنه و یا از قدرت و منابع خوبی برخوردار نباشه این زمان میتونه بیشتر و بیشتر هم بشه! در این لحظه اتفاقی که میوفته این هست که stack بلوکه میشه و تا زمانی که درخواست ما به سمت سرور نره و پاسخی نیاد این بلوکه شدن ادامه پیدا میکنه و چیزدیگری در stack جای نخواهد گرفت و اجرا نخواهد شد و در نتیجه در چنین لحظه ایی به عنوان مثال اگر کاربر برروی دکمه ایی کلیک کنه هیچ اتفاقی نخواهد افتاد! چرا ؟ چون stack بلوکه شده و هیچ کد دیگری تا زمان اتمام آن فریم اجرا نخواهد شد.اما این مشکل به لطف ajax حل شده و دیگه مشکل دردسر سازی نیست!در زبان های multi threaded languages مثل ruby این موضوع تا حدی هندل شده و آنچنان مشکل دردسر سازی نیست، اما در زبان single threaded مثل جاوااسکریپت این یک مشکل جدی و حائز اهمیت است چراکه در این زبان ها تا یک استک تمام نشود و چیزی را return نکن فریم بعدی اجرا نخواهد شد.ساده ترین راه استفاده از asynchronous callbacks هستش. که به این معنا هست که ما یک تکه کد را اجرا میکنیم و به آن یک کالبک فانکشن میدهیم که قراره بعدا و بعد از گذشت یک زمانی و یا گوش به زنگ یک چیز دیگری اجرا شود. مثل setTimeout و Promises و .....تمام این کالبک فانکشن ها در لحظه اجرا نمیشوند و طبیعتا نمیشه مثل sync function هایی مثل console.log آنهارا به استک اضافه کنیم. به عنوان مثال تکه کد زیر که یک فانکشنی هست که درخاستی را به سمت سرور ارسال میکند و بعد جواب آنرا میخواهد در کنسول نمایشد دهد:async http requestو با این ترتیب دیگه کال استک بلوکه نمیشه و یجورایی با جدا کردن این قسمت، ما به ران تایم جاوااسکریپت اجازه میدهیم درحالیکه هنوز منتظر جواب این api call هست. بقیه کدهارو هم اجرا کنه و از بلاک شدن کال استک جلوگیری کنه. حال سوال اینجاست که چطور با وجود اینکه کد خطی و با سرعت زیادی اجرا میشود و به سرعت از متغیر response عبور میکند، بازهم به محض آمدن پاسخی از سمت سرور دیتای دریافتی را به ما نشان میدهد ؟اینجاست که browser apis وارد میشوند. browser api که با ++c جهت هندل کردنه چیزهایی مثل DOM Events و http request ها و setTimeout و ..... ساخته شده است.اما این browser api ها نمیتونن خودشون کد را در کال استک قرار دهند و اگر قرار بود اینکار را خودشان انجام دهند ممکن بود بصورت رندم و بدون ترتیب خاص و قابل انتظار ما انجام شوند، این موضوع را همان صف پیام یا Queue که بالاتر درموردش صحبت کردیم انجام میدهد و راه را به browser api نشان میدهد.اما Event Loop چیشد ؟ browser api این کدهایی که بعد از گذشت زمانی قرار بود اجرا شوند و ما منتظر پاسخشان بودیم و همچنین نمیخاستیم stack بلاک شود را درون Queue میبرد و حالا Event Loop موظف هست که که این کالبک های توی Queue را اجرا کند و انهارا به استک انتقال دهد ( دقت کنید که زمانی اینکار را انجام میدهد و اون کالبک را از Queue به درون استک انتقال میدهد که استک خالی باشد. به مثال زیر توجه کنید:فکر میکنید ترتیب اجرای کد بالا به چه شکل است ؟ فکر میکنید اول 1 و بعد 2 و بعد 3 در کنسول چاپ میشود ؟ اگر اینطور فکر میکنید سخت در اشتباه هستید. اگر دقت کنید به کمی قبل تر. به شما گفتم که Event Loop فقط زمانی یک کالبک فانکشن مثل setTimeout که browser api حساب میشود را درون استک میبرد که کال استک خالی شده باشد. بنابر این کد بالا به این صورت خروجی اش به ما نمایش داده خواهد شد. اول 1 و بعد 3 و بعد 2حال فکر میکنم تا خواندن انتهای توضیحات Event Loop هم به خوبی Queue را درک کرده باشید و هم Event Loop را و امیدوارم این مطلب برای شما مفید واقع شده باشه و از خواندن آن لذت برده باشید. در آینده ایی نزدیک و یا شاید هم دور برای تکمیل این مطلب ، مطالب دیگری را یا در قالب پستی این چنینی و یا در قالب یک فیلم کوتاه در یوتیوب یا اینستاگرام برایتان قرار خواهم داد تا پرونده این بخش بسته بشه و بریم سراغ باقی بخش جذابه باقی مونده این زبان شیرین و لذت بخش :)