DEV Community

Brandon Zhang
Brandon Zhang

Posted on

Using JavaScript Debounce to Only Send Network Request When User Stops Typing

When we have a search input and need to listen for keyup event from users and then send network request to our server using fetch() api.

const searchProducts = async (e) => {
  const name = e.target.value
  const res = await fetch(`https://someapi.com/products?name=${name}`)
  const users = await res.json()
}

const input = document.querySelector('input.search')
input.addEventListener('keyup', searchProducts)
Enter fullscreen mode Exit fullscreen mode

But by doing this, we would be send fetch() request every time when user is typing a letter in the search input, and that is not ideal. We can refactor the codes to only send search request when user has finished typing.

const searchProducts = async (e) => {
  const name = e.target.value
  const res = await fetch(`https://someapi.com/products?name=${name}`)
  const products = await res.json()
}

let timeout // 1
const debounce = (e) => {
  clearTimeout(timeout) // 3
  timeout = setTimeout(() => { // 2
    searchProducts(e.target.value)
  }, 1000)
}

const input = document.querySelector('input.search')
input.addEventListener('keyup', debounce)
Enter fullscreen mode Exit fullscreen mode

If you're a junior developer like me, this might looks
a bit confusing. What's going here is that we're setting a global variable timeout (1), and then assign a setTimeout to it (2), but we need to clear that timeout otherwise searchProducts() will still get fired if user hasn't finished typing, meaning that 1s has passed since user has started typing, but that doesn't necessarily mean user has finished typing.

Another way we can look at it is setTimeout() will return a timeoutID.

/** 
 * assume we type our first letter in search box,
 * now debounce() will run once
 * we've set our first timeout and its timeoutID
 * will be returned and stored on timeout variable
 * timeoutID = 1 at this moment
 */
let timeout 
const debounce = (e) => {
  timeout = setTimeout(() => { // timeoutID = 1
    searchProducts(e.target.value)
  }, 1000)
}

/** 
 * now we type our second letter in search box,
 * debounce() will run again
 * we've set our second timeout and its timeoutID
 * will be returned and stored on timeout variable
 * timeoutID = 2 at this moment
 */
let timeout 
const debounce = (e) => {
  timeout = setTimeout(() => { // timeoutID = 2
    searchProducts(e.target.value)
  }, 1000)
}

// but our first timeout is still valid,
// if we don't cancel that, it will still run
// when 1s has passed since it's been created
// that is why we need to clear the timeout before 
// we set the next timeout
let timeout 
const debounce = (e) => {
  clearTimeout(timeout)
  timeout = setTimeout(() => { // timeoutID = 3
    searchProducts(e.target.value)
  }, 1000)
}
Enter fullscreen mode Exit fullscreen mode

That's it. Hope it's helpful.

Top comments (0)