غیرهمزمانی در جاوااسکریپت
احتمالا خیلی وقتا واستون پیش اومده از کسی یه کاری رو بخواین و پشت سرهم ازش بپرسین کار من انجام شد؟! معمولا اینطور مواقع جوابی که میگیرین اینه : “۱۰ تا دست که ندارم، صبر کن انجامش میدم”. حالا به همین مثال از دید یه برنامه نویس نگاه کنید. وقتی برنامتون اجرا میشه، تا اولین کار تموم نشه دومی شروع نمیشه، تا دومی تموم نشه سومی شروع نمیشه. حالا چی میشد اگه برنامهی ما ۱۰ تا دست داشت و میتونست کارا رو با هم انجام بده !!
(یه نکتهی کوچیک : در مورد multi tasking صحبت میکنیم نه multi thread 🙂 )
برای اینکه مفهوم غیرهمزمان بودن بهتر جابیفته از یه مثال استفاده میکنیم. فرض کنید برنامتون در حال خوندن از دیتابیس هست(یا هرکار وقت گیر دیگه ای)، خب خوندن از دیتابیس در مقایسه با کارهای عادی که برنامتون انجام میده به نسبت وقت گیرتر هست و همچنین به عملیات IO مربوط میشه. در حالت همزمان(synchronous) برای اجرای بقیه برنامه باید منتظر بمونیم تا کارمون با دیتابیس تموم بشه و خروجی بگیریم، حالا هرچقدر که وقت بگیره؛ اما در حالت غیرهمزمان(asynchronous) وقتی برنامه شروع به خوندن از دیتابیس کرد، بقیه برنامه به کار خودش ادامه میده و منتظر جواب کار قبلی نمیمونه. اینکار از نظر سرعت خیلی کمکمون میکنه.
برای روشنتر شدن موضوع یکم کد بنویسیم.
for (var i = 0; i < 10; i++) { console.log("1", i); }
کد بالا به ترتیب اعداد ۰ تا ۹ رو تو خروجی چاپ میکنه.(کنار هرکدوم عدد ۱ رو هم چاپ میکنه، واسه اینکه بتونیم از بقیه برنامه تفکیکشون کنیم). خب میبینیم که برنامه ما داره به ترتیب کار میکنه؛ دلیلش اینه که کد ما هنوز داره به صورت sync کار میکنه. توی مثال پایین یکم کدمون رو تغییر میدیم ببینیم چه اتفاقی میفته.
for (var i = 0; i < 10; i++) { if (i == 1 || i == 2) { setTimeout(function() { console.log("1", i); }, 1); } else { console.log("1", i); } }
گفتیم که اگه i برابر ۱ یا ۲ شد، با استفاده از تابع settimeout یه وقفهی ۱ میلیثانیهای توی اجراشون بنداز. نتیجهی این کد این میشه که اعداد ۰ و ۳ و ۴ و … و ۹ چاپ میشن و در اخر دوبار عدد ۱۰ چاپ میشه!! برای اینکه دلیلشو بفهمیم باید با مفهومی به نام event loop آشنا بشیم.
در واقع event loop صفی هست برای callback function ها. این صف از نوع FIFO هست و همونطور که میدونید توی این صف هر چیزی اول وارد شه، اول خارج میشه. وقتی ما تابع settimeout رو اجرا میکنیم داریم یه عضو به event loop اضافه میکنیم. توی مثال بالا وقتی i برابر ۱ شد و settimeout فراخونی شد، یه عضو وارد event loop شد. وقتی i برابر ۲ شد و settimeout فراخونی شد، یه عضو دیگه وارد event loop شد و پشت سر قبلی قرار گرفت. حالا عضوهای event loop چه زمانی شروع به کار میکنن؟ وقتی که کار خودشون رو انجام دادن(۱ میلیثانیه صبر کردن) و همچنین جاوااسکریپت مشغول انجام کار دیگهای نباشه، یعنی وقتی که حلقهی ما تموم شد و دیگه کد جاوااسکریپتی برای اجرا نداشتیم. وقتی حلقهی ما به آخر رسید، i برابر ۱۰ شد و از حلقه خارج شدیم پس حالا که قراره i رو چاپ کنیم، ۱۰ چاپ میشه.
کد زیر رو اجرا کنید تا مسئله بیشتر جا بیفته.
for (var i = 0; i < 10; i++) { if (i == 1 || i == 2) { setTimeout(function() { console.log("1", i); }, 1); } else { console.log("1", i); } } for (var i = 0; i < 10; i++) { console.log("2", i); }
میبینید که حلقه اول اجرا میشه، حلقه دوم اجرا میشه، وقتی که کاری نموند انجام بشه، کدهایی که وارد event loop کرده بودیم شروع به اجرا میکنن. یعنی عدد ۱ و ۲ مربوط به حلقه اول، در آخر چاپ میشن.
مثالی که زدیم مربوط به timeout بود، ولی برای مثلا ارتباط با دیتابیس قضیه به این صورته که ما درخواستمون به دیتابیس رو میفرستیم، هروقت جواب آماده بود، در event loop قرار میگیره. اینجوری کارای مربوط به IO که به جاوااسکریپت مربوط نیستن جداگونه انجام میشن و هروقت جوابشون اومد اجرا میشن.
(البته کارکرد event loop یکم متفاوته)
دیدگاهتان را بنویسید
برای نوشتن دیدگاه باید وارد بشوید.