mehdi sedighi
mehdi sedighi
خواندن ۴ دقیقه·۵ سال پیش

جهنم callback در جاوااسکریپت

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

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

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

نحوه مواجه زبان ها سنتی با دستورات نوع دوم:

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

نحوه مواجه زبان های جدید با این دستورات:

برخلاف زبان های سنتی زبان های جدید دست پری برای مواجه با این شرایط دارند. در برخورد با خطا ها در زبان های جدید استفاده از بلوک try کاملا اجباری است و برنامه نویس نمی تواند از زیر بار آن شانه خالی کند. در برخورد با سرعت کم اجرا دستورات در زبان های برنامه نویسی جدید اجرای ناهمزمان را پیشنهاد می کنند. در زبان مانند جاوااسکریپت پردازنده منتظر اجرای کامل دستورات کند نمی شود به سرعت به سراغ دستورات بعدی می رود. این امر موجب بروز مشکلاتی می شود. بنابر رسم زبان های برنامه نویسی دستورات به صورت متوالی اجرا می شوند تا دستور بالا به پایان نرسیده به سراغ دستور بعدی نمی روند اما در دنیایی پردازنده های چند هسته ای و محیط های پر تقاضا معطلی برای پاسخ مساوی با کاهش راندمان و زمان پاسخ دهی است.

راه حل زبان جاوا اسکریپت

برای حل این مشکل زبان برنامه نویسی جاوا اسکریپت توابع callback را پیشنهاد می کند فرض کنید دو تابع A و B داریم تابع A سرعت اجرای پایینی دارد اما خروجی آن باید به تابع ‌B ارسال شود به عبارتی تابع B وابسته به تابع A است. callback برای این مشکل پیشنهاد می کند تابع B را به عنوان یک آرگومان به تابع A ارسال کنید. زمانی که تابع A کارش به پایان رسید دو مقدار به عنوان خروجی بر می گرداند خروجی اول که نشان می دهد آیا تابع A اجرایش را با موافقیت به پایان رسانده دومین خروجی نتیجه پردازش موفق تابع A است که باید توسط تابع B استفاده شود. در این روش پردازنده منتظر اجرای کامل تابع A نمی شود هر وقت داده مورد نظر در تابع A فراهم شد و تابع خروجی را تولید کرد رویدادی را ایجاد می کند تا به پردازنده اتمام کار را اطلاع دهد آن وقت پردازنده شروع به اجرای تابع B با استفاده از داده فراهم شده می کند.

مشکل جهنم callback

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

کدهایی مانند کد فوق در زبان هایی مانند جاوااسکریپت مرسوم هستند این کد ها برای رسیدن به راندمان بیشتر خوانایی کد را کاهش می دهند. و این در تناقض با یک اصل اساسی برنامه نویسی یعنی ساده نویسی است. برای حل این مشکل راه حل های متعددی پیشنهاد شده است یکی از این راه حل ها استفاده از کلید واژه های Async/Await است. بااستفاده از این کلید ها کد فوق به صورت زیر بازنویسی می شود:

دستور await در ابتدای فراخوانی یک تابع تعیین می کند اجرای این تابع به صورت ناهمزمان خواهد بود و باید اجرای سایر خط های برنامه تا زمان اجرای کامل این تابع به تعویق بیفتد. سوالی که پیش می آید این است که این امر همان رهیافت سنتی است یعنی صبر کرد ظاهرا بله اما با یک تفاوت در این جا پردازنده منتظر اجرا نمی شود به سراغ کارهای دیگر می رود زمانی که منابع مورد نیاز تابع فراهم شد به پردازنده خبر داده می شود تا اجرای تابع را کامل کند.

نکته نهایی این که توابعی که درون آنها این گونه دستوراتی وجود دارد در هنگام تعریف آنها باید کلمه async در ابتدای نام آنها قرار گیرد که به معنی توابع ناهمزمان هستند.

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

https://blog.hellojs.org/asynchronous-javascript-from-callback-hell-to-async-and-await-9b9ceb63c8e8



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