skip to Main Content

I have a search bar that on "input", makes an API request based on the value of the search bar. The API response is used to populate search suggestions.

The issue I have is that the API requests are made asynchronously – so the responses are not guaranteed to come back in the same order you sent them. This means the search suggestions could end up being incorrect.

E.g. if I quickly type wh in the search bar, the page will send two API requests:

/search?q=w
/search?q=wh

If the /search?q=w request comes back last, then the suggestions will be populated with the results for w – when it should show the results for wh instead.

What’s the best way to solve this? I was thinking of storing a timestamp each time I update the search suggestions, and if the function notices a later timestamp, it will not overwrite the suggestions. This should work, but I’m wondering if there’s a nicer / more canonical way to do this.

Here’s a MWE:

let searchBar = document.getElementById("search-bar");
let suggestionsHTML = document.getElementById("suggestions")

searchBar.addEventListener("input", fillSuggestions);
async function fillSuggestions(event) {
  let resp = await fetch("/search?" + new URLSearchParams({
    q: searchBar.value,
  }))
  let suggestionsJSON = await resp.json()

  suggestionsHTML.innerHTML = ''
  for (let result of suggestionsJSON) {
    // create new result element & insert into suggestions
    addSuggestion(suggestionsHTML, result)
  }
}

2

Answers


  1. Adding debounce is a good way to solve this.

    const debounce = (func, delay) => {
      let debounceTimer;
      return (...args) => {
        clearTimeout(debounceTimer);
        debounceTimer = setTimeout(() => func(...args), delay);
      };
    }
    

    And then use it like this, here we are adding a delay of 300 milliseconds

    const searchBar = document.getElementById("search-bar");
    const suggestionsHTML = document.getElementById("suggestions");
    
    // Wrap the fillSuggestions function with debounce
    searchBar.addEventListener("input", debounce(fillSuggestions, 300));
    
    const fillSuggestions = async (event) => {
      const resp = await fetch("/search?" + new URLSearchParams({
        q: searchBar.value,
      }));
      const suggestionsJSON = await resp.json();
    
      suggestionsHTML.innerHTML = '';
      for (let result of suggestionsJSON) {
        // create new result element & insert into suggestions
        addSuggestion(suggestionsHTML, result);
      }
    }
    

    You can read about debounce here.

    Login or Signup to reply.
  2. You can use the debounce technique to trigger API requests after the user stops typing after some time interval. These methods also lower the server load.

    You can use this freecodecamp blog: https://www.freecodecamp.org/news/javascript-debounce-example/

    if you don’t want to use that method. You can then use the AbortController Signal to cancel past requests and initiate new API requests.

    let controller;
    
    async funcation getData(){
    if(controller != undefined) controller.abort();
    
    controller = new AbortController();
    
    const res = await fetch('url',{ signal: controller.signal });
    // do rest...
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search