خیلی از مواقع توی اپلیکیشن های سمت وب پیش میاد که ما نیاز داریم رفتار های نامناسب کاربر در برناممون رو مدیریت کنیم و ازشون جلوگیری کنیم. در این پست به معرفی یک سناریو پرتکرار میپردازیم و میبینیم که چطور میشه با دو مفهوم Debouncing و Throttling در شرایط مناسب از پس این مشکلات بر اومد.
به صفحۀ ساده بالا توجه کنید. در اینجا یک button با شناسۀ fetch-btn داریم و متن "Fetch Data" که قرار است با کلیک بر روی این button تعدادی داده از یک API دریافت کنیم و در بخش ":Results" نمایش دهیم.
در این موقعیت کاربر میتواند با کلیک های متوالی بر روی دکمۀ Fetch Data تعداد درخواست هایی که به سمت سرور فرستاده میشود را به طور قابل توجهی افزایش دهد. همچنین میدانیم با یک بار ارسال درخواست (یا حداقل تعداد انگشت شمار درخواست های متوالی در صورت بروز خطاهای احتمالی)، کاربر به هدف خود خواهد رسید (این موقعیت معمولا در فرم های ثبت نام و یا احراز هویت به وجود میآید).
برای دریافت داده ها، تابع زیر را در اختیار داریم که از یک REST API فِیک (Fake) با نام Jsonplaceholder استفاده میکند و از مسیر /posts تعداد 100 داده را برمیگرداند:
برای اینکه این تابع را به دکمۀ Fetch Data متصل کنیم نیاز داریم از addEventListener با ایونت click استفاده کنیم و این تابع را در بخش handler این ایونت، اجرا کنیم، یعنی:
اما میدانیم که با هربار کلیک کاربر این تابع دوباره و دوباره اجرا شده، و تعداد درخواست های فرستاده شده سمت سرور (با دستور fetch) به شدت بالا خواهد رفت.
شاید از خودتون بپرسین که اگه دکمۀ مورد نظرمون رو disable کنیم مشکلمون حل میشه، ولی این رو درنظر داشته باشین که این attribute ها به سادگی توسط کاربر قابل تغییر هستن و در بعضی موارد نیاز به مدیریت این وضعیت با خود JavaScript داریم.
البته برای اینکه داده ها در باکس Results نمایش داده بشوند خیلی ساده میتونیم یک تابع به شکل زیر تعریف کنیم و در بخش onSuccess به تابع fetchPosts پاس بدیم:
یک راه حل میتواند این باشد که به کاربر اجازۀ کلیک های متوالی را بدهیم و بعد از گذشت یک زمان معین از آخرین کلیک، شروع به اجرای تابع مورد نظر کنیم. این دقیقاً همان چیزی است که Debouncing به ما ارائه میکند. به قطعه کد زیر توجه کنید:
در این پیاده سازی، یک reference به آخرین دفعهای که تابع debounce شده قرار بود که اجرا شود خواهیم داشت (مقدار بازگرداننده شده از setTimeout)، و به سادگی با استفاده از یک setTimeout آن تاخیر مورد نظر را ایجاد میکنیم و با هردفعه اجرا شدن تابع reference قبلی را پاک میکنیم (با استفاده از clearTimeout) تا تاخیر مورد نظر از اول ایجاد شود. در اینجا بعد از نیم ثانیه (500 میلی ثانیه) از آخرین کلیک، تابع fetchPosts اجرا میشود و نتایج در باکس مربوط به Results نمایش داده میشوند.
راه حل دیگر این است که با کلیک اول کاربر، تابع را اجرا کنیم و سپس تصمیم بگیریم که با کلیک بعدی این تابع را اجرا کنیم یا خیر (میتوانیم با قرار دادن شرط های مختلف از اجرای اضافه جلوگیری کنیم که در این مورد به علت توضیح اصل مطلب این کار را انجام ندادیم). این راه حل هم همان چیزیست که در Throttling با آن مواجهیم:
این مورد به شدت در افزایش بازدهی برنامه موثر است و همچنین از بار اضافۀ ارسال شونده به سمت سرور جلوگیری میکند.
این اولین پست من در ویرگول هست و امیدوارم که مفید بوده باشه 🚀.