Debouncing در جاوا اسکریپت – با ایجاد عملکرد تکمیل خودکار در React توضیح داده شده است
سلام خوانندگان، امیدوارم که عالی باشید! من برگشتم با آموزش دیگری در مورد توسعه وب. اگر شما فردی هستید که از توسعه برنامه های وب با جاوا اسکریپت و React لذت می برید، پس این پست برای شما مناسب است.
وقتی یک برنامه جدید را وارد مرحله تولید می کنید، می خواهید مطمئن شوید که کاربر پسند است. عملکرد یک وب سایت بخش کلیدی از تجربه کاربر است. هر کاربر می خواهد وب سایت و محتوای آن به سرعت بارگذاری شود. هر ثانیه ارزشمند است و می تواند باعث شود که کاربر دیگر هرگز از وب سایت شما بازدید نکند.
در این راهنما، ما قصد داریم یک تکنیک بسیار مهم در جاوا اسکریپت را که به عنوان debouncing شناخته می شود، درک کنیم. سپس، من به شما نشان خواهم داد که چگونه عملکرد تکمیل خودکار را در React با debouncing پیاده سازی کنید.
اکنون، برای اینکه بیشترین بهره را از این آموزش ببرید، فرض میکنم که شما دانش اولیه جاوا اسکریپت را دارید. اگر نیاز به شروع یا مرور دارید، در اینجا چند منبع برای شما آورده شده است:
اصول جاوا اسکریپت را بیاموزید – کتابچه راهنمای مبتدیان
گواهی الگوریتمهای جاوا اسکریپت و ساختار دادههای freeCodeCamp
فهرست مطالب:
نحوه پیاده سازی Debouncing در جاوا اسکریپت
از Case of Debouncing استفاده کنید
Debouncing چیست؟
Debouncing یک استراتژی است که برای بهبود عملکرد یک ویژگی با کنترل زمانی که یک تابع باید اجرا شود، استفاده می شود.
Debouncing یک تابع را می پذیرد و آن را به یک تابع به روز شده (debounced) تبدیل می کند تا کد داخل تابع اصلی پس از مدت زمان مشخصی اجرا شود.
اگر تابع بازگردانده شده دوباره در آن بازه زمانی فراخوانی شود، تایمر قبلی بازنشانی میشود و تایمر جدیدی برای این فراخوانی تابع شروع میشود. فرآیند برای هر فراخوانی تابع تکرار می شود.
یک مثال به شما در درک بهتر کمک می کند. بیایید یک تابع fun()
بگیریم. ما قصد داریم این تابع بعد از 500 میلی ثانیه اجرا شود.
function fun() { console.log('This is a function') }
پس از debouncing، یک تابع جدید debouncedFun()
برگردانده می شود. اکنون، هر زمان که debouncedFun()
فراخوانی کنید، پس از 500 میلی ثانیه فراخوانی می شود.
اگر پس از اولین تماس، ظرف 500 میلیثانیه بعدی دوباره آن را فراخوانی کنید، تایمر قبلی بازنشانی میشود و تایمر جدیدی برای فراخوانی عملکرد دوم شروع میشود. اگر به فراخوانی تابع در 500 میلیثانیه ادامه دهید، فرآیند تکرار میشود.
نحوه پیاده سازی Debouncing در جاوا اسکریپت
بیایید نحوه پیاده سازی debouncing در جاوا اسکریپت را درک کنیم. ابتدا نیازهای خود را تحلیل می کنیم. چه رفتاری از تابع debounced می خواهیم؟
تاخیر در اجرای تابع با زمان معینی، delay
.
اگر عملکرد دوباره فراخوانی شد، تایمر را بازنشانی کنید.
برای بازگرداندن یک تابع، یک تابع جداگانه خواهیم داشت که مرجع تابع و تاخیر را به عنوان پارامتر می پذیرد و یک تابع بازگردانده شده را برمی گرداند.
function debounce(func, delay) { return () => {} // return debounced function }
این تابع فقط یک بار برای برگرداندن یک تابع بازگردانده شده فراخوانی می شود و به نوبه خود در کد بعدی استفاده می شود.
برای به تاخیر انداختن یک تابع تا چند میلی ثانیه، به سادگی می توانیم از تابع setTimeout
در جاوا اسکریپت استفاده کنیم.
function debounce(func, delay) { return () => { setTimeout(() => { func() }, delay) } }
این امر فراخوانی تابع را با میلی ثانیه delay
می اندازد. اما این ناقص است زیرا فقط نیاز اول را برآورده می کند. چگونه به رفتار دوم دست یابیم؟
بیایید یک متغیر timeout
ایجاد کنیم و آن را به مقدار برگشتی متد setTimeout
اختصاص دهیم. متد setTimeout
یک شناسه منحصر به فرد را به timeout برمی گرداند که توسط متغیر timeout
نگهداری می شود.
function debounce(func, delay) { let timeout=null return () => { timeout=setTimeout(() => { func() }, delay) } }
هر بار که setTimeout
را فراخوانی می کنید، شناسه متفاوت است. ما از این متغیر timeout
برای تنظیم مجدد تایمر استفاده خواهیم کرد.
اما چگونه از خارج از متد debounce()
به timeout
دسترسی پیدا کنیم؟ همانطور که قبلا ذکر شد، debounce()
تنها یک بار برای برگرداندن یک تابع debounced استفاده می شود. این به نوبه خود، منطق انحرافی را انجام می دهد.
سپس، چگونه تابع debounced حتی اگر خارج از تابع debounce()
استفاده شود، به timeout
دسترسی دارد؟ خوب، از مفهومی به نام بسته شدن استفاده می کند.
بسته شدن در جاوا اسکریپت چیست؟
در جاوا اسکریپت، یک تابع داخلی همیشه به متغیرهای محلی تابع خارجی دسترسی دارد. در مورد ما، تابع داخلی به timeout
دسترسی دارد که در متد debounce()
دارای محدوده سطح تابع است.
اما زمانی که تابع خارجی این تابع درونی را برمی گرداند، تابع درونی همچنان ارجاع به متغیرهای محلی تابع بیرونی را مدت ها پس از اتمام اجرای تابع بیرونی نگه می دارد. این مفهوم بسته شدن است.
بیایید بسته شدن را با یک مثال درک کنیم.
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
بسته می شود و فقط این تابع می تواند از متغیر استفاده کند و هیچ کس دیگری نمی تواند. ما نمی توانیم به طور واضح به متغیر دسترسی داشته باشیم.
برای کسب اطلاعات بیشتر در مورد بسته شدن می توانید این آموزش مبتدی را تحلیل کنید.
بازگشت به Debouncing
بیایید برگردیم به جایی که متوقف کردیم:
function debounce(func, delay) { let timeout=null return () => { timeout=setTimeout(() => { func() }, delay) } }
در اینجا، جاوا اسکریپت از بسته شدن استفاده میکند تا هر بار که از تابع بازگردانده شده استفاده میکنیم، دسترسی به timeout
را حفظ کند.
بیایید از این به نفع خود استفاده کنیم. از آنجایی که debouncedFun()
در هر فراخوانی تابع به متغیر timeout
یکسانی دسترسی دارد، میتوانیم یک شرط برای تحلیل وجود مهلت زمانی قبلی اضافه کنیم. ما به سادگی می توانیم این کار را با یک تحلیل تهی، if(timeout !== null)
یا if(timeout)
انجام دهیم.
سپس از متد clearTimeout()
برای لغو وقفه قبلی استفاده می کنیم و در نتیجه تایمر را تنظیم مجدد می کنیم.
قبل از شروع یک بازه زمانی جدید عبارت زیر را اضافه کنید:
if(timeout) clearTimeout(timeout)
هنگامی که زمان بازنشانی شد، یک مهلت زمانی جدید برای فراخوانی تابع فعلی شروع میشود که شناسه آن به timeout
اختصاص داده میشود. این فرآیند برای فراخوانیهای تابع بعدی که به دلیل بسته شدن به همان timeout
دسترسی دارند، تکرار میشود.
function debounce(func, delay) { let timeout=null return () => { if(timeout) clearTimeout(timeout) timeout=setTimeout(() => { func() }, delay) } }
با این کار، نیاز دوم خود را برآورده کرده ایم - یعنی تنظیم مجدد تایمر و شروع یک تایمر جدید. زمان استفاده از این تابع بازگردانده شده است.
اجازه دهید fun()
به متد debounce()
با تاخیر 500 میلی ثانیه منتقل کنیم.
const debouncedFun = debounce(fun, 500)
debouncedFun()
اساساً fun()
با رفتار debouncing. بیایید این تابع را در بازه های زمانی مختلف فراخوانی کنیم تا عملکرد خود را آزمایش کنیم.
debouncedFun() setTimeout(debouncedFun, 300) setTimeout(debouncedFun, 900)
اولین فراخوانی تابع فورا انجام می شود. دو مورد دیگر به ترتیب پس از 300 میلیثانیه و 900 میلیثانیه ساخته میشوند. آیا می توانید خروجی را حدس بزنید؟
کد چاپ می شود This is a function
. بیایید بفهمیم چرا. در اینجا، پس از برقراری اولین تماس، fun()
پس از 500 میلی ثانیه اجرا می شود. اما دومی در 300 میلیثانیه ساخته شده است که تایمر را بازنشانی میکند و تایمر جدید را شروع میکند.
500 میلیثانیه گذشت و متد fun()
اجرا میشود. سپس در 900ms، فراخوانی تابع دیگری انجام می شود. این دوباره بعد از 500 میلی ثانیه fun()
را اجرا می کند.
هنوز یک پیشرفت کوچک وجود دارد که باید انجام دهیم. منطق ما آرگومان های تابع را در نظر نمی گیرد. بیایید fun()
با fun(a, b)
جایگزین کنیم.
function fun(a, b) { console.log(`This is a function with arguments ${a} and ${b}`) }
برای ترکیب آرگومانها در حین بازگرداندن، یک تابع بازگردانده شده را برگردانید که آرگومانها را میپذیرد.
function debounce(func, delay) { let timeout=null return (...args) => { if(timeout) clearTimeout(timeout) timeout=setTimeout(() => { func(...args) timeout=null }, delay) } }
با استفاده از عملگر spread، هر آرگومان ارسال شده به تابع debounced به عنوان یک آرایه در متغیر args
ذخیره می شود. سپس، همان آرایه args
را برای فراخوانی تابع واقعی با آرگومان های ارسال شده پخش کنید.
const debouncedFun=debounce(fun, 500) debouncedFun(2,3)
کد بالا This is a function with arguments 2 and 3
بعد از 500 میلی ثانیه چاپ می کند.
از Case of Debouncing استفاده کنید
بیایید ببینیم که چگونه debouncing در برنامه های کاربردی استفاده می شود. متداول ترین مورد استفاده از انحراف، عملکرد تکمیل خودکار است. حتماً وبسایتهای زیادی را دیدهاید که در یک فیلد ورودی تایپ میکنید و با تایپ کردن آنها فهرستی از نتایج را نشان میدهد.
در اینجا یک مثال از جستجوی گوگل آورده شده است:
جستجوی گوگل جدیدترین و متداول ترین عبارات جستجو شده را نشان می دهد. اطلاعات بیشتر از کش مرورگر واکشی می شود. اما، چندین وبسایت برای واکشی دادهها از پایگاه داده، با سرور پشتیبان تماسهای API برقرار میکنند.
این را می توان به راحتی با گفت ن یک رویداد onchange
به عنصر input
و پیاده سازی منطق واکشی در کنترل کننده رویداد پیاده سازی کرد. اما یک مشکل جزئی در این مورد وجود دارد.
به مثال زیر توجه کنید:
وقتی کلمه مطلق را تایپ می کنم، هر بار که مقدار فیلد ورودی تغییر می کند، یک درخواست API ایجاد می شود. ما 8 درخواست API را در چند میلی ثانیه انجام می دهیم که بار زیادی را روی سرور باطن وارد می کند و می تواند باعث مشکلات عملکرد شود.
در حالت ایدهآل، میخواهیم نتایج تکمیل خودکار را مدتی پس از اتمام تایپ کاربر نشان دهیم. در اینجا، کاربر یکباره مطلق تایپ کرده است، پس به جای نمایش نتایج هر بار که ورودی تغییر می کند، می توانیم آنها را پس از اتمام تایپ کاربر نشان دهیم - یعنی می توانیم مقداری تاخیر بین تغییر ورودی و نمایش نتایج اضافه کنیم. .
پس ، ما فقط زمانی که کاربر تایپ کلمه خود را تمام می کند و نه در هر تغییر ورودی، تماس های API را برقرار می کنیم. این باعث کاهش تعداد تماس های API و بهبود عملکرد می شود. ما می توانیم این رفتار را با انحراف به دست آوریم.
بیایید نحوه پیاده سازی عملکرد تکمیل خودکار را در React بدانیم.
مثال تکمیل خودکار
برای ایجاد پروژه از create-react-app
(یا یک ابزار ساخت مدرن مانند Vite) استفاده کنید. کد دیگ بخار موجود را حذف کنید. نیازی به نصب وابستگی اضافی نیست. دستور npm start
را برای شروع پروژه اجرا کنید. می توانید کد کامل را در GitHub پیدا کنید.
من یک سرور Node برای واکشی داده ها برای برنامه راه اندازی کرده ام. می توانید آن را در مخزن Git پیدا کنید. دستور node server
را اجرا کنید تا شروع شود. من قصد ندارم کد Node.js را نشان دهم زیرا خارج از محدوده این آموزش است.
بیایید با پیاده سازی شروع کنیم. ما یک قابلیت تکمیل خودکار ساده خواهیم نوشت. برنامه باید فهرستی از شهرهایی را نشان دهد که شامل یک رشته ورودی تایپ شده توسط کاربر است.
جزء برنامه
ابتدا به یک عنصر input
برای پذیرش ورودی کاربر و یک محفظه نتایج برای نتایج جستجو نیاز داریم. یک کنترل کننده رویداد را به عنصر input
که یک تابع async
است وصل کنید زیرا منطق واکشی را شامل می شود.
function App() { const [data, setData] = useState(null) const loadData = async (event) => { } return ( <div className="App"> <input type="text" onChange={(e) => loadData(e)}/> {data && data.length !== 0 && <div className="results-container"> {data.map(item => ( <div key={item.id} className="result-item"> <p> {item.city} </p> </div> ))} </div>} </div> ); }
داده ها به عنوان حالت ذخیره می شوند و نتایج فقط در صورتی نشان داده می شوند که داده ها خالی نباشند. من از CSS برای این آموزش صرف نظر می کنم، می توانید آن را در Git Repo پیدا کنید.
مدیریت رویداد
تابع loadData()
داده های ما را واکشی می کند و پاسخ را به عنوان حالت ذخیره می کند.
const loadData = async (event) => { const value=event.target.value if(value === '') { setData(null) return } const response=await fetch(`http://localhost:8000/data/${value}`) const res=await response.json() setData(res) }
اگر مقداری وارد نشد، به سادگی از تابع خارج شوید. در غیر این صورت، درخواست را به نقطه پایانی سرور گره ارسال کنید. این تابع هر بار که ورودی تغییر می کند فراخوانی می شود، پس ما این تابع را حذف می کنیم.
پیاده سازی Debounce با استفاده از یک هوک سفارشی
ما منطق debouncing را در داخل یک هوک سفارشی می نویسیم. مزیت قلاب های سفارشی این است که می توانید از همان منطق در سراسر برنامه خود استفاده مجدد کنید. انجام این کار بسیار توصیه می شود.
یک پوشه جدید custom-hooks
ایجاد کنید و داخل آن یک فایل useDebounce.js
ایجاد کنید. همانطور که قبلا توضیح داده شد، متد useDebounce()
باید یک تابع و تاخیر را به عنوان پارامتر بگیرد و تابع debounced را برگرداند.
const useDebounce = (func, delay) => { let timeout=null return (...args) => { if(timeout) clearTimeout(timeout) timeout=setTimeout(() => { func(...args) }, delay) } } export default useDebounce
اکنون در داخل کامپوننت برنامه، یک بار این متد را فراخوانی کنید تا loadDataDebounced()
را دریافت کنید.
const loadDataDebounced = useDebounce(loadData, 400)
ما از این روش جدید به عنوان کنترل کننده رویداد برای عنصر input
استفاده خواهیم کرد.
<input type="text" onChange={(e) => loadDataDebounced(e)}/>
خروجی
یک رشته جستجو را در داخل عنصر input
وارد کنید تا کد ما را آزمایش کنید.
همانطور که در تب Network می بینید، به جای سه درخواست، فقط یک درخواست ارسال می شود. این باعث می شود عملکرد جستجو بسیار بهتر شود.
نتیجه
در این آموزش یاد گرفتید که debouncing چیست و چگونه اجرا می شود. Debouncing اجرای تابع را تا زمان معینی به تاخیر می اندازد و در صورت فراخوانی مجدد تابع، تایمر قبلی را بازنشانی می کند.
Debouncing از مفهوم مهم بسته شدن استفاده می کند. من کمی از اجرا منحرف شدم تا توضیح دهم که بسته شدن چیست. این می تواند یک مفهوم گیج کننده برای مبتدیان باشد، پس برای درک آن وقت بگذارید. بسته شدن به شما امکان می دهد حتی پس از اتمام اجرای یک تابع، با متغیرهای محلی کار کنید.
پس از آن، من به شما یک مورد استفاده محبوب از debouncing را نشان دادم، عملکرد تکمیل خودکار. عملکرد این ویژگی را میتوان با حذف کردن بهبود داد. من همچنین به شما نشان دادم که چگونه تکمیل خودکار را در React پیاده سازی کنید و از debouncing با هوک های سفارشی استفاده کنید. امیدوارم این به شما در پروژه های آینده کمک کند.
اگر نمی توانید مطالب را درک کنید یا توضیح را رضایت بخش نمی دانید، به من اطلاع دهید. ایده های جدید همیشه قدردانی می شوند! با خیال راحت با من در توییتر ارتباط برقرار کنید. تا آن زمان، خداحافظ!
ارسال نظر