@@ -262,112 +262,3 @@ class SearchInputElement extends HTMLInputElement {
262262 }
263263}
264264` ` `
265-
266- ### Using signals to debounce or throttle methods
267-
268- _Debouncing_ or _Throttling_ are techniques whereby you delay an action for a fixed time period, discarding any events
269- between them. These techniques are useful within UI as a way to delay an expensive operation like a network fetch or
270- something CPU intensive like sorting many items. Consider a list filter input, that shows and hides thousands of items
271- depending on if they match the text input.
272-
273- As debouncing & throttling are based on time, an ` AbortController` isn't needed. Timeouts are such a common pattern that
274- ` AbortSignal .timeout ()` can be used as a quick way to create a signal that will abort after some time has passed.
275-
276- Throttling and debouncing are variations on the same broader concept of limiting the times an action gets executed. If
277- you're calling a function 10 times in a row, at 50ms intervals, a throttle of 100ms would ensure that the action is only
278- run every 100ms, whereas a debounce would defer each call until there hadn't been one for more 100ms or more. To
279- illustrate this concept we can look at a timeline:
280-
281- ` ` `
282- | ---- - | ---- - | ---- - | ---- - | ---- - | ---- - | ---- - | ---- - |
283- Source 50 100 200 250 350 400
284- throttle (100 ) 100 200 350
285- debounce (100 ) 100 250 400
286- ` ` `
287-
288- You can think of throttling as disregarding some calls but acting on others, whereas debounce waits for quiet periods.
289- To throttle a method, you'll create the signal every time it is aborted, and execute the behavior when the timer has
290- aborted:
291-
292- ` ` ` js
293- class ListFilterInputElement extends HTMLInputElement {
294- static define (tag = " list-filter-input" ) {
295- customElements .define (tag, this , { extends: " input" })
296- }
297-
298- connectedCallback () {
299- this .addEventListener (" input" , this )
300- }
301-
302- timeout = 100
303-
304- #throttleSignal = null
305-
306- handleEvent () {
307- // Don't do anything if the timer has yet to time out
308- if (this .#throttleSignal && ! this .#throttleSignal .aborted ) return
309-
310- // The time has been aborted, so make a new timer for next time
311- this .#throttleSignal = AbortSignal .timeout (this .timeout )
312-
313- // Execute the action
314- this .filer ()
315- }
316-
317- filter () {
318- for (const el of this .list .children ) {
319- el .hidden = el .textContent .includes (this .input )
320- }
321- }
322- }
323- ` ` `
324-
325- _Debouncing_ would add a **delay** and so needs to act on the timeout happening, not some time after. Using the
326- ` aborted` event can queue up work for when the timer ends:
327-
328- ` ` ` js
329- class ListFilterInputElement extends HTMLInputElement {
330- static define (tag = " list-filter-input" ) {
331- customElements .define (tag, this , { extends: " input" })
332- }
333-
334- connectedCallback () {
335- this .addEventListener (" input" , this )
336- }
337-
338- timeout = 100
339-
340- #debounceSignal = null
341-
342- async handleEvent () {
343- // Don't do anything if the timer has run out
344- if (this .#debounceSignal? .aborted ) return
345-
346- // Renew the signal
347- this .#debounceSignal = AbortSignal .timeout (this .timeout )
348-
349- // Schedule work for after debouncement
350- this .#debounceSignal .addEventListener (
351- " abort" ,
352- (event ) => {
353- // Check to see that new work hasn't been scheduled
354- if (event .target === this .#debounceSignal) {
355- // Clear out the signal so new work can be schedule
356- this .#debounceSignal = null
357-
358- // Execute the action
359- this .filter ()
360- }
361- },
362- { once: true }
363- )
364- }
365-
366- filter () {
367- for (const el of this .list .children ) {
368- el .hidden = el .textContent .includes (this .input )
369- }
370- }
371- }
372- ` ` `
373-
0 commit comments