Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to delay/start/debounce fetching data until user stops typing?

If I get enough users on my application, sending an ajax request with each keystroke is an effective way to bring the server to its knees (not to mention possibly making the client application feel quite sluggish). On implementing a symbol search box with two options (DB Search and Web Api Search). While I am typing the symbol (ex: AAPL - aple stock) in the search box the fetch() request is sent each time over the network. To avoid it I tried to use setTimeout() but fetch() request is sent multiple times, anyway. How to delay/start/debounce fetching the request until user stops typing in input area to send only one fetch() request?

HTML:

<label for="symbolTags">Symbol: </label>
  <input type="text" id="symbolTags" name="symbol">

  <label for="api">Select Search Api: </label>
  <select id="api" name="routes_api">
    <option value="search">Web Search Api</option>
    <option value="dbsearch">DB Search Api</option>
  </select>

JavaScript:

const symbolTags = document.querySelector('#symbolTags')
const symbolTagsOptions = document.querySelector('#api')

const urlsObject = {
  dbsearch: '/dbsearch/',
  search: '/search/'
}

symbolTags.oninput = function () {
  let symbolTagsOptionsValue = symbolTagsOptions.value
  let arg = urlsObject[symbolTagsOptionsValue]

  // Init a timeout variable to be used below
  let timeout = null
  // Clear the timeout if it has already been set.
  // This will prevent the previous task from executing
  // if it has been less than <MILLISECONDS>
  clearTimeout(timeout)
  // Make a new timeout set to go off in 2000ms
  timeout = setTimeout(function () {
    requestSymbolSearch(arg)
  }, 2000)
}

function requestSymbolSearch(arg) {
  getData(arg)
    .then(data => {
      console.log(data)
      $('#symbolTags').autocomplete({
        source: data.map(item => item.symbol),
        autoFocus: true
      })
    })
    .catch(error => console.error('Error:', error))
}

function getData(url) {
  let curValueSymbol = symbolTags.value
  let urlPlus = `${url}${curValueSymbol}`
  console.log(urlPlus)
  return fetchData(urlPlus)
}

async function fetchData(urlPlus) {
  const dataResponse = await fetch(urlPlus)
  const dataJson = await dataResponse.json()
  return dataJson
}

Here is the console result:

fetch result

Here is the network result:

enter image description here

like image 667
John John Avatar asked Jan 22 '19 04:01

John John


People also ask

What is a debounce delay?

Debounce delays the processing of a function bound to a certain user input event until a certain amount of time has passed. In other words the function is only executed once per specific user input event, even it the event is triggered multiple times.

How do you execute a function only after the user stops typing react?

Solution. To avoid that problem, we better execute a function in proper timing which means after a user stops typing for a while. And setTimeout helps us to do that. The setTimeout() method calls a function or evaluates an expression after a specified number of milliseconds.

How do I use debounce onChange?

function debounce(fn, delay) { var timer = null; return function() { var context = this, args = arguments; clearTimeout(timer); timer = setTimeout(function() { fn. apply(context, args); }, delay); }; } var SearchBox = React. createClass({ render: function() { return <input type="search" name="p" onChange={this.

What is debounce interval?

The amount of time, in seconds, that elapses before the downlink interfaces are brought up after a state change of the uplink interfaces.


1 Answers

This is commonly solved by debouncing the event; which collapses multiple calls in a given timeframe to just one:

// Debounce function from: https://stackoverflow.com/q/24004791/1814486
const debounce = (func, wait, immediate) => {
  let timeout

  return function() {
    const context = this, args = arguments
    const later = function() {
      timeout = null
      if (!immediate) func.apply(context, args)
    }

    const callNow = immediate && !timeout
    clearTimeout(timeout)
    timeout = setTimeout(later, wait)
    if (callNow) func.apply(context, args)
  }
}
  
// If there's not another `input` within 500ms log the value,
// otherwise ignore the event.
document.querySelector('#input').addEventListener('input', debounce(() => {
  console.log(input.value)
}, 500))
<input id="input" placeholder="Type fast here.."/>
like image 166
nicholaswmin Avatar answered Oct 21 '22 08:10

nicholaswmin