Throttling در جاوا اسکریپت چیست؟ با استفاده از Simple React Case توضیح داده شده است
خوش آمدید، توسعه دهندگان همکار! امروز، ما یک بار دیگر در حال تحلیل جاوا اسکریپت و توسعه وب و یادگیری در مورد throttling هستیم.
به عنوان یک توسعه دهنده، کاربرپسند کردن وب سایت خود مهم است. این امر تا حد زیادی به موفقیت محصول کمک می کند و بخش مهمی از تجربه کاربر، عملکرد وب سایت است.
در یک آموزش قبلی ، در مورد چگونگی بهبود عملکرد هر ویژگی با استفاده از تکنیکی به نام debouncing بحث کردم. و می توانید از تکنیک مشابهی به نام throttling استفاده کنید، اما در سناریوی کمی متفاوت. نحوه اجرای throttling را در این مقاله خواهید آموخت.
برای این راهنما، من فرض می کنم که شما دانش پایه ای از جاوا اسکریپت دارید. اگر مبتدی هستید نگران نباشید - من توضیحات ساده و مفصلی را برای راهنمایی شما ارائه کرده ام. پس ، بیایید درست در آن شیرجه بزنیم!
فهرست مطالب
Throttling چیست؟
Throttling تکنیکی است که برای محدود کردن سرعت فراخوانی یک تابع استفاده می شود. Throttling یک تابع را طوری تبدیل می کند که فقط یک بار در یک بازه زمانی مشخص می توان آن را فراخوانی کرد.
بیایید با یک مثال این را بفهمیم. بیایید یک تابع fun()
بگیریم:
function fun() { console.log('This is a function') }
ما قصد داریم این تابع را طوری تغییر دهیم که فقط یک بار در 500 میلی ثانیه فراخوانی شود. پس ، throttling fun()
به عنوان ورودی می گیرد و یک تابع تغییر یافته (throttled) throttledFun()
را برمی گرداند که فقط 500 میلی ثانیه پس از اجرای تابع قبلی قابل اجرا است.
وقتی چند بار در عرض 500 میلیثانیه throttledFun()
فراخوانی میکنید، fun()
فقط اولین بار اجرا میشود. برای اجرای مجدد fun()
باید 500 میلی ثانیه صبر کنید. این پس از هر فراخوانی تابع بعدی اتفاق می افتد. پس ، fun()
فقط می توان یک بار در هر 500 میلی ثانیه فراخوانی کرد.
نحوه پیاده سازی Throttling در جاوا اسکریپت
بیایید ابتدا بفهمیم که قصد داریم با دریچه گاز به چه چیزی برسیم:
اولین بار فوراً تابع را فراخوانی کنید.
پس از هر تماس، از فراخوانی مجدد تابع برای یک دوره زمانی خاص جلوگیری کنید.
پس از گذشت آن بازه زمانی، تابع را می توان دوباره فراخوانی کرد.
برای انجام همه این کارها، اجازه دهید ابتدا یک تابع کمکی ایجاد کنیم که یک تابع throttled را برمی گرداند:
function throttle(func, delay) { return () => {} // return throttled function }
برای هر مورد استفاده، تابع throttled به جای fun()
اصلی استفاده می شود.
بیایید با فراخوانی تابع مانند زیر شروع کنیم:
function throttle(func, delay) { return () => { func() } }
هنگامی که تابع فراخوانی شد، میخواهیم از delay
مجدد آن برای مدت زمان معینی جلوگیری کنیم. پس از گذشت زمان، قصد داریم عملکرد را رفع انسداد کنیم. ما می توانیم با استفاده از setTimeout
به این رفتار دست یابیم.
در حال حاضر، اجازه دهید setTimeout
را خالی نگه داریم. در عرض یک دقیقه متوجه خواهید شد که چگونه کار می کند.
setTimeout(() => {}, delay)
در مرحله بعد، یک timeout
متغیر را اعلام می کنیم که تنها یک بار در تابع خارجی (که تابع throttle()
است مقداردهی اولیه می شود. متد setTimeout
یک شناسه زمانبندی منحصر به فرد را برمیگرداند که میتوانیم از آن برای شناسایی مهلت استفاده کنیم. شناسه مهلت زمانی فعلی را به timeout
اختصاص می دهیم.
function throttle(func, delay) { let timeout=null return () => { func() timeout=setTimeout(() => { // do something }, delay) } }
از آنجایی که timeout
شامل شناسه مهلت زمانی فعلی است، یک شرط را در شروع تابع throttled اضافه می کنیم تا قبل از فراخوانی تابع اصلی func()
تحلیل کنیم که آیا مهلت زمانی وجود دارد یا خیر.
function throttle(func, delay) { let timeout=null return () => { if(!timeout) { func() timeout=setTimeout(() => { // do something }, delay) } } }
در ابتدا، timeout
پوچ است، پس تابع اجرا می شود. سپس تابع throttled یک بازه زمانی جدید را شروع می کند و آن را به متغیر timeout
اختصاص می دهد. در فراخوانی تابع بعدی، قبل از فراخوانی func()
تحلیل میکند که آیا مهلت زمانی وجود دارد یا خیر. اگر یک مهلت از قبل وجود داشته باشد، func()
اجرا نمی کند.
اما پس از گذشت مدت زمان delay
چه اتفاقی می افتد؟ در داخل setTimeout
باید کاری انجام دهیم که امکان فراخوانی مجدد func()
را فراهم کند. از آنجایی که ما timeout
برای کنترل فراخوانی های تابع استفاده می کنیم، پس از میلی ثانیه delay
آن را روی null قرار می دهیم.
timeout=setTimeout(() => { timeout=null }, delay)
اکنون وقتی تابع را فراخوانی میکنید، اجرا میشود و با شروع یک تایم اوت جدید، فرآیند تکرار میشود. ما با موفقیت عملکرد را متوقف کردیم.
اما یک چیز اساسی وجود دارد که ما هنوز از آن غافل هستیم. در فراخوانی تابع فعلی، هنگامی که setTimeout
به متغیر timeout
اختصاص می دهیم، برای متغیر بعدی فرض می کنیم که timeout
هنوز معتبر است و مقدار مورد نظر ما را حفظ می کند - حتی اگر متغیر در داخل تابع throttle()
اعلان شده باشد.
چگونه تابع داخلی می تواند مدت ها پس از اتمام اجرای تابع throttle()
به متغیر دسترسی داشته باشد؟ از مفهومی به نام closure استفاده می کند. بیایید یک مسیر انحرافی سریع برای بازدید از این مفهوم داشته باشیم.
بسته شدن در جاوا اسکریپت چیست؟
در جاوا اسکریپت، یک تابع درونی همیشه به متغیرهای محلی تابع خارجی دسترسی دارد. در مورد ما، تابع داخلی به timeout
دسترسی دارد که محدوده سطح تابع در متد throttle()
دارد.
اما زمانی که تابع خارجی این تابع درونی را برمی گرداند، تابع درونی همچنان ارجاع به متغیرهای محلی تابع بیرونی را مدت ها پس از اتمام اجرای تابع بیرونی نگه می دارد. این مفهوم بسته شدن است.
بیایید بسته شدن را با یک مثال درک کنیم.
function outerFunction() { const x = 5; return () => { console.log(x); } } const inner = outerFunction(); inner(); // prints 5 // console.log(x) Throws reference error
در اینجا، اگر inner()
را فراخوانی کنیم، کد بدون هیچ خطایی اجرا میشود و شماره 5 را چاپ میکند. اما اگر بخواهیم مستقیماً به x
دسترسی پیدا کنیم، جاوا اسکریپت یک خطای مرجع ایجاد میکند.
در اینجا، inner()
روی x
بسته می شود و فقط این تابع می تواند از متغیر استفاده کند و هیچ کس دیگری نمی تواند. ما نمی توانیم به طور واضح به متغیر دسترسی داشته باشیم.
برای کسب اطلاعات بیشتر در مورد بسته شدن می توانید این آموزش مبتدی را تحلیل کنید.
بازگشت به گاز
بیایید از همان جایی که متوقف کردیم ادامه دهیم.
function throttle(func, delay) { let timeout=null return () => { if(!timeout) { func() timeout=setTimeout(() => { timeout=null }, delay) } } }
دیدهایم که جاوا اسکریپت هر بار که تابع throttled را فراخوانی میکنیم، از بستهها استفاده میکند تا دسترسی به timeout
حفظ کند.
با این کار، ما یک پیاده سازی اساسی از throttling تابع داریم.
بیایید آن را با استفاده از روش fun()
بالا آزمایش کنیم و آن را با تأخیر 500 میلیثانیه مهار کنیم. این تابع فقط باید هر 500 میلی ثانیه یک بار اجرا شود.
const throttledFun = throttle(fun, 500)
بیایید throttledFun()
به روش های مختلف فراخوانی کنیم و ببینیم چگونه اجرا می شود.
throttledFun(); // This will execute immediately throttledFun(); // This will be ignored setTimeout(() => { throttledFun(); // This will also be ignored }, 300); setTimeout(() => { throttledFun(); // This will execute }, 600);
اولین فراخوانی تابع بلافاصله اجرا می شود. برای 500 میلی ثانیه بعدی (تاخیر throttling در این مثال)، مهم نیست که چند بار throttledFun()
تماس بگیرید، هیچ اتفاقی نمی افتد.
پس ، دومین فراخوانی تابع و سومین فراخوانی اجرا نمیشوند، زیرا در فاصله 500 میلیثانیه از تماس اول رخ میدهند. پس از گذشت 500 میلی ثانیه، فراخوانی تابع بعدی - یعنی آخرین مورد - اجرا می شود، زیرا تماس بعد از 500 میلی ثانیه انجام می شود.
پس ، خروجی زیر را چاپ می کند:
This is a function This is a function // Printed after 600ms
راه حل هنوز کامل نشده است. رویکرد ما آرگومان های تابع را در نظر نمی گیرد. پس ، اجازه دهید fun()
برای داشتن دو آرگومان تغییر دهیم:
function fun(a,b) { console.log(`This is a function with args ${a} and ${b}`) }
برای ترکیب آرگومان ها، از عملگر spread ...
استفاده کنید و همه آرگومان ها را در یک متغیر args
ذخیره کنید:
function throttle(func, delay) { let timeout=null return (...args) => { if(!timeout) { func(...args) timeout=setTimeout(() => { timeout=null }, delay) } } }
حالا دوباره throttledFun()
با آرگومان هایی مانند این فراخوانی کنید:
throttledFun(2,3);
This is a function with args 2 and 3
.
از Case of Function Throttling استفاده کنید
بیایید ببینیم که چگونه throttling در کاربردهای عملی استفاده می شود. دکمهای را میگیریم که وقتی کاربر روی آن کلیک میکند، با سرور باطن تماس میگیرد. هر بار که فردی روی دکمه کلیک می کند، یک تماس API برقرار می شود.
اما درخواست API ممکن است کمی طول بکشد، و اگر کاربر دوباره روی دکمه کلیک کند یا در مدت زمان کوتاهی مکرراً روی آن کلیک کند، تماسهای API بیشتر و بیشتر انجام میشود که میتواند سرور را بیش از حد بارگیری کند. برای جلوگیری از این رفتار، از تابع throttling استفاده می کنیم. بیایید این را با React پیاده سازی کنیم.
پروژه را راه اندازی کنید
create-react-app
در ترمینال خود اجرا کنید یا از یک ابزار مدرن ساخته شده مانند Vite برای ایجاد برنامه React خود استفاده کنید. کد دیگ بخار موجود را حذف کنید. نیازی به نصب وابستگی اضافی نیست. دستور npm start
را برای شروع پروژه اجرا کنید. می توانید کد کامل را در GitHub پیدا کنید.
من یک سرور Node برای واکشی داده ها برای برنامه راه اندازی کرده ام. می توانید آن را در مخزن Git پیدا کنید. دستور node server
را اجرا کنید تا شروع شود. من قصد ندارم کد Node.js را نشان دهم زیرا خارج از محدوده این آموزش است.
بیایید با اجرا شروع کنیم.
جزء برنامه
در کامپوننت App، بیایید یک دکمه با یک کنترل کننده onClick
ایجاد کنیم که یک تماس API با سرور Node برقرار می کند.
function App() { const fetchData = async () => { const resp = await fetch("http://localhost:8000/data"); return resp.json(); }; const handleClick = () => { fetchData().then((data) => { console.log(data); }); }; return ( <div className="App"> <button onClick={handleClick}>Click Me</button> </div> ); } export default App;
حالا بیایید این دکمه را مکرراً کلیک کنیم.
در اینجا، هر بار که دکمه کلیک می شود، یک تماس API برقرار می شود. پس ، اگر کاربر به طور مکرر روی دکمه کلیک کند، تعداد زیادی تماس API در یک ثانیه انجام می شود. این ممکن است سرور را بیش از حد بارگذاری کند.
برای مقابله با این موضوع، باید از فراخوانی API با هر کلیک روی دکمه جلوگیری کنیم. بیایید ببینیم چگونه می توان با دریچه گاز به این امر دست یافت.
اجرای Throttling با استفاده از یک هوک سفارشی
ما منطق throttling را در داخل یک قلاب سفارشی می نویسیم. از آنجایی که ممکن است در چندین مکان در برنامه خود نیاز به throttling داشته باشید، توصیه می شود منطق را در یک هوک سفارشی قرار دهید.
یک پوشه جدید به نام custom-hooks
ایجاد کنید. در داخل آن، یک فایل useThrottle.js
ایجاد کنید. در داخل فایل، تابع جدید useThrottle()
ایجاد و صادر کنید. روش باید تابع و تاخیر را به عنوان پارامتر بگیرد و تابع throttled را برگرداند.
const useThrottle = (func, delay) => { let timeout = null; return (...args) => { if (timeout) { return; } func(...args); timeout = setTimeout(() => { timeout = null; }, delay); }; }; export default useThrottle;
اکنون در داخل کامپوننت App، این متد را فراخوانی کرده و کنترل کننده کلیک handleClick()
و تاخیر 1000 میلی ثانیه را ارسال کنید.
const handleClickThrottled = useThrottle(handleClick, 1000);
ما از این تابع به عنوان کنترل کننده رویداد برای دکمه خود استفاده خواهیم کرد.
<button onClick={handleClickThrottled}>Click Me</button>
خروجی
پس از کلیک مکرر روی دکمه به مدت دو ثانیه، تنها دو تماس API برقرار می شود.
با محدود کردن تعداد دفعات فراخوانی API های شما، throttling عملکرد برنامه شما را بهبود می بخشد.
نتیجه
در این آموزش یاد گرفتید که throttling چیست و چگونه آن را پیاده سازی کنید. Throttling به شما امکان می دهد تا سرعت اجرای یک تابع را در یک دوره خاص کنترل کنید.
Throttling از مفهوم مهمی به نام closures استفاده می کند. آنها به شما اجازه می دهند حتی پس از اتمام اجرای یک تابع با متغیرهای محلی کار کنید. بسته شدن می تواند برای مبتدیان کاملاً گیج کننده باشد، پس وقت خود را با آنها صرف کنید.
پس از آن، من یک مورد استفاده رایج از throttling را به شما نشان دادم، که در آن میتوانید کنترل کنید که چند بار یک تماس API با کلیک چند دکمه انجام شود. من از هوک های سفارشی برای پیاده سازی throttling در React استفاده کردم. این به بهبود عملکرد برنامه های کاربردی وب کمک می کند. امیدوارم این به شما در پروژه های آینده شما کمک کند.
اگر نمی توانید مطالب را درک کنید یا توضیح را رضایت بخش نمی دانید، به من اطلاع دهید. ایده های جدید همیشه قدردانی می شوند! با خیال راحت با من در توییتر ارتباط برقرار کنید. تا آن زمان، خداحافظ!
ارسال نظر