جاوا اسکریپت چه جوری کار میکنه؟ مروری بر engine , runtime و call stack

سری "جاوا اسکریپت چه جوری کار میکنه؟" بازگردن مقاله های Alexander Zlatkov به زبون خودم هست. هدف از این کار در مرحله ی اول یادگیری خودم و انتشار این مقاله به فارسیه.

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

اهمیت این نوشته برای من محتواش هست نه طرز بیان یا نگارشش. ممنون میشم اگر که هم در محتوا هم در طرز بیان یا نگارش اشکالی هست بیان کنید تا بتونم بهترش کنم.

بازگردانی شده از : How JavaScript works: an overview of the engine, the runtime, and the call stack


هرچقدر که جاوا اسکریپت داره معروف و معروف تر میشه, تیم های زیادی دارن ازش تو قسمت های مختلف استکشون مثل front-end, back-end, hybrid apps, embedded devices و خیلی مورد های دیگه استفاده میبرن.

این نوشته اولین مطلب این سریه و موضوعیتش در مورد فهم عمیق تر جاوا اسکریپت و این که در واقع داره چه جوری کار میکنه هست:‌ ما فکر میکنیم با دونستن اجزای تشکیل دهنده جاوا اسکریپت و تعامل بینشون شما میتونید کد ها و برنامه های بهتری بنویسید. همچنین ما نکاتی که تو ساخت SessionStack, اپلیکیشن سبک جاوا اسکریپتی که باید خیلی چفت و بست دار و سریع باشه تا بتونه رقابت بکنه رو بیان میکنیم.

همین طور که از استار های GitHut.info مشخصه, جاوا اسکریپت بالاتر از همه قرار میگیره تو مورد های مخزن های فعال و تعداد پوش ها به گیتهاب. حتی تو مورد های دیگه هم خیلی عقب نیست از بقیه.

(Check out up-to-date GitHub language stats).
(Check out up-to-date GitHub language stats).


اگر پروژه ای داره خیلی به جاوا اسکریپت وابسته میشه به این معنی هست که دولوپر ها باید هر چه بیشتر از زبان و همه ی اون چیزی که اکوسیستم براشون فراهم کرده با فهم عمقی تری از اجازی داخلی استفاده ببرن تا بتونم یه برنامه خفن بسازن.

اینجور که معلوم هست, دولوپر های زیادی هستن که دارن روزانه از جاوا اسکریپت استفاده میکنن و نمیدونن چه اتفاقاتی داره اون پشت مشت ها میوفته. (من فقط ترجمه کردم. نزنید آدمو)

بررسی کوتاه

تقریبا همه اسم V8 Engine رو شنیدن یه تصوری ازش دارن, اکثر مردم هم میدونن که جاوا اسکریپت تک ترد هست یا این که داره از صف کال بک ها استفاده میکنه.

تو این نوشته, درمورد همه این موضوعات به طور مفصل صحبت میکنیم و توضیح میدیم که جاوا اسکریپت چه جوری کار میکنه. با دونستن این جزیات, شما میتونید برنامه های بهتر و non-blocking رو بنویسید که به خوبی داره از api های فراهم شده استفاده میکنه.

اگر شما تازه کار هستید تو جاوا اسکریپت, این نوشته به شما کمک میکنه که متوجه بشید چرا جاوااسکریپت عجیب تر رفتار میکنه نسبت به زبان های دیگه.

و اگر شما یه جاوا اسکریپت دولوپر با تجربه هستید, امیدوار کنندست که این نوشته به شما یه دید جدید در مورد چگونه کار کردن رانتایم جاوا اسکریپت که هر روز دارید باهاش کار میکنید بده.

موتور جاوا اسکریپت

یه نمونه معروف موتور جاوا اسکریپتی Googles V8 Engine هست. این موتور در دل Chrome و Node.js تعبیه شده. این یه نمایش ساده از این موتور هست که چه ریختیه:

این موتور تشکیل شده از دو کامپوننت اصلی هست:

  • Memory Heap - جایی که اختصاص دادن حافظه صورت میگیره
  • Call Stack - این جا هم فریم های استک نگه داری میشه به هنگام اجرای برنامه

رانتایم

ای پی آی هایی که تو مرگر هست که تقریبا توسط همه دولوپر های جاوا اسکریپت استفاده شده مثل setTimeout‌, با این حال حتی این api ها توسط موتور جاوا اسکریپت فراهم نشدن .

خو پس از کجاش داره میاد ؟

به نظر میرسه که واقعیت در مورد این ها یکم پیچیدست.

خب ما موتور جاوا اسکریپت رو داریم ولی در حقیقت چیز های دیگه هم مثلا Web APIs که مرورگر اونها رو فراهم کرده مثل DOM, AJAX و همین تابع setTimeout و خیلی چیزای دیگه. و در کنار این ها event loop و calback queue معروف.

پشته اجرایی یا Call Stack

جاوا اسکریپت یه زبون تک تردی هست. به این معنی که فقط یه دونه پشته اجرایی داره. پس فقط در لحظه یه کارو میتونه انجام بده.

پشته ی اجرایی یه ساختمون داده هست که از دستور هایی که باید اجرا بشن رو تو خودش نگه میداره عملا مشخص میکنه کجای اجرای برنامه هستیم. اگر وارد اجرای یه تابع بشیم اون تابع رو بالای این پشته گذاشتیم و هروقت هم در اون تابع ریترن کردیم از بالای پشته برش میداریم. این همه کاری هست که استک اجرایی میتونه انجام بده

یه مثال ببینیم. کد زیر رو نگاه کنید.

function multiply(x, y) {
    return x * y;
}
function printSquare(x) {
    var s = multiply(x, x);
    console.log(s);
}
printSquare(5);

وقتی موتور جاوا اسکریپت شروع به اجرای این کد میکنه, در اون لحظه شروع پشته اجرایی خالیه. بعد از اون قدما های بعدی به شکل زیر هست:

هر ورودی به پشته اجرایی رو میگن Stack Frame.

و این دقیقا نحوه ی تولید stack traces رو نشون میده وقتی یه اکسپشنی رخ میده. به طور پایه stack trace وضعیت پشته ی اجرایی رو بیان میکنه در زمانی که اکسپشنی رخ داده. به کد زیر نگاه کنید:

function foo() {
    throw new Error('SessionStack will help you resolve crashes :)');
}
function bar() {
    foo();
}
function start() {
    bar();
}
start();

اگه این کد تو کروم اجرا بشه و اسم فایل اجرایی رو هم بزاریم foo.js استک تریسی مثل عکس پایین باید درست کنه:

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

function foo() {
    foo();
}
foo();

وقتی موتور شروع به اجرای این کد میکنه از تابع foo کارشو استارت میزنه. این تابع بازگشتی هست و خودش رو صدا میزنه بدون هیچ شرط اتمامی. پس تو هر مرحله ی اجرا هی تابع foo به پشته اضافه میشه. میشه یه چیزی مثل عکس زیر:

بالاخره تعداد فراخوانی تابع داخل پشته اجرایی از حد مجاز میگذره و مرورگر تصمیم یه اروری ایجاد کنه که میتونه این شکلی باشه:

اجرای کد ها تو حالت تک تردی خیلی راحته چون دیگه لازم نیست به سناریو های پیچیده ای که تو حالت مالتی ترد پیش میاد فکر کنیم. برای مثال dead locks.

ولی خب اجرا برنامه تنها روی یک ترد هم محدودیت های خودش رو داره. وقتی جاوا اسکریپت یه پشته ی اجرای داره وقتی دستور ها کند اجرا میشن چه اتفاقی میوفته؟

هم زمانی و چرخه رویداد ها --- Concurrency & the Event Loop

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

شاید بپرسید خب مشکلش چیه؟ مشکل اینجاست که وقتی پشته اجرایی تابعی برای اجرا داشته باشه مرورگر درواقع هیچکار دیگه ای نمیتونه انجام بده و بلاک میشه. این یعنی مرورگر نمیتونه رندر کنه و کد دیگه ای رو اجرا کنه. یه جا گیر کرده. و خب این مشکل درست میکنه وقتی نیاز به یه UI ترو تمیز داریم.

و این تنها مشکل هم نیست. وقتی مرورگر شما شروع به پراسس تعداد زیادی تسک از پشته ی اجرای میکنه , ممکنه برای مدت طولانی دیگه نتونه پاسخ گو باشه. و اکثر مرورگر ها هم این موقع ها یه اروری رو نشون میدن که آیا میخوای این صفحه رو ببندیم ؟

حالا این یه تجربه ی کاربری خوب نیست که انتظار میره. درسته ؟

خب ما چه جوری میتونیم کد های سنگین رو اجرا بگیریم بدون این که UI رو بلاک کنیم و مرورگر رو بترکونیم؟ راه حل asynchronous callbacks هست. این موضوع تو نوشته بعدی بیشتر توضیح داده میشه.