Функция Debounce в JavaScript на примере приложения для поиска

В этом руководстве вы узнаете о функции debounce JavaScript, которая ограничивает количество вызовов функции, и о том, как использовать ее для повышения производительности приложения.

Чтобы понять функцию debounce, мы создадим приложение для поиска в Википедии, используя метод программирования debounce в JavaScript.

Создайте структуру папок проекта

Сначала создайте новую папку с именем wikipedia-search, в которой будут храниться файлы проектов.

Во-вторых, создайте три папки внутри папки wikipedia-search с js, css и img. В этих папках будут храниться файлы JavaScript, CSS и изображений соответственно.

В-третьих, создайте style.css в папке css и app.js в папке js. Также загрузите следующее изображение и скопируйте его в папку img. Вы будете использовать логотип для создания пользовательского интерфейса приложения.

Функция debounce в JavaScript

Наконец, создайте файл index.html в корневой папке.

Структура проекта будет выглядеть следующим образом:

Структура проекта

Создайте HTML-страницу

Откройте файл index.html и добавьте следующий код:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Wikipedia Search</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <header>
        <img src="./img/wikipedia-logo.png" alt="wikipedia">
        <h1>Wikipedia Search</h1>
        <input type="text" name="searchTerm" id="searchTerm" placeholder="Enter a search term...">
    </header>
    <main id="searchResult"></main>
    <script src="js/app.js"></script>
</body>
</html>

В этом HTML-файле:

  • Сначала создайте ссылку на файл style.css в разделе <head>.
  • Во-вторых, добавьте <script>, src которого ссылается на файл app.js, и поместите его прямо перед </body>.
  • В-третьих, добавьте два раздела в тело HTML-страницы. Первый раздел показывает логотип Википедии, заголовок и окно поиска. Второй раздел включает <main>, который будет отображать результат поиска.

Скопируйте код CSS

Перейдите к файлу style.css, скопируйте его код и вставьте его в файл style.css в папке css.

Обработка входных событий

Сначала выберите элементы <input> и результаты поиска с помощью метода querySelector() :

const searchTermElem = document.querySelector('#searchTerm');
const searchResultElem = document.querySelector('#searchResult');

Во-вторых, установите фокус на элемент <input>, вызвав метод focus() :

searchTermElem.focus();

В-третьих, подключите прослушиватель событий input для элемента <input> :

searchTermElem.addEventListener('input', function(event) {
    console.log(event.target.value);
});

Если вы наберете текст в элементе <input>, вы увидите, что происходит событие input, которое отображает текст в консоли.

Например, когда вы вводите debounce в элементе <input> :

Обработка входных событий

… вы увидите следующие тексты в консоли:

Текст на консоли

Получение результатов поиска с помощью API Википедии

API Википедии довольно прост. Для этого не требуется ключ API.

Чтобы получить темы по поисковому запросу, вам нужно добавить параметр запроса srsearch :

&srsearch=<searchTerm>

на следующий URL-адрес:

https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10

… и отправить запрос HTTP GET.

Например, вы можете получить темы, связанные с ключевым словом debounce, отправив HTTP- GET на следующий URL-адрес:

https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10&srsearch=debounce

Кстати, вы можете открыть указанный выше URL-адрес в веб-браузере, чтобы увидеть ответ.

Из JavaScript вы можете использовать API выборки, доступный во всех современных веб-браузерах, для отправки HTTP-запроса GET.

Далее создается функция search(), которая принимает поисковый запрос, отправляет HTTP- GET в Википедию и показывает результаты поиска в консоли:

const search = async(searchTerm) => {
    try {
        const url = `https://en.wikipedia.org/w/api.php?

Как это работает:

  • Сначала создайте URL-адрес API, добавив параметр запроса srsearch в конечную точку:
const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10&srsearch=${searchTerm}`;
  • Во-вторых, используйте метод fetch() для отправки HTTP-запроса GET. Поскольку метод fetch() возвращает промис, вам нужно использовать ключевое слово await для ожидания ответа.

Промис, возвращаемый функцией fetch(), имеет много методов, один из них — json(). Метод json() также возвращает другое обещание, которое преобразуется в результат в формате JSON.

Из-за ключевого слова await вам нужно пометить функцию search() как async, например:

const search = async(searchTerm) = {
   /// ...
};

Возвращаемый объект метода json() имеет множество свойств. А чтобы получить результаты поиска, вам нужно получить доступ к свойству searchResults.query.search.

Чтобы протестировать метод search(), вы вызываете его в прослушивателе событий input следующим образом:

searchTermElem.addEventListener('input', function(event) {
    search(event.target.value);
});

Ниже показан полный файл app.js :

const searchTermElem = document.querySelector('#searchTerm');
const searchResultElem = document.querySelector('#searchResult');

searchTermElem.select();

searchTermElem.addEventListener('input', function(event) {
    search(event.target.value);
});

const search = async(searchTerm) => {
    try {
        const url = `https://en.wikipedia.org/w/api.php?

Теперь, если вы откроете файл index.html и введете ключевое слово debounce в элементе ввода, вы увидите в консоли следующие результаты:

Вывод показывает, что функция search() выполняется для каждого введенного вами символа. Она вызывает API для каждого ввода текста, что неэффективно.

Чтобы ограничить количество запросов, вы будете отправлять запросы API только при необходимости. Другими словами, вы отправите запрос API только после того, как пользователи приостановят или перестанут печатать в течение определенного периода времени, например полсекунды.

Для этого вы можете использовать функции setTimeout() и clearTimeout() :

  • Когда пользователи вводят символ, используйте функцию setTimeout(), чтобы запланировать выполнение функции search() через определенный период времени.
  • Если пользователи продолжают печатать, отмените этот таймер с помощью функции clearTimeout(). В случае, если пользователи приостанавливают или перестают печатать, дайте таймеру выполнить запланированную функцию поиска.

Ниже показана новая версия функции search() :

let timeoutId;

const search =(searchTerm) => {
    // reset the previous timer
    if(timeoutId) {
        clearTimeout(timeoutId);
    }

    // set up a new timer
    timeoutId = setTimeout(async() => {
        try {
            const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10&srsearch=${searchTerm}`;
            const response = await fetch(url);
            const searchResults = await response.json();

            // show the search result in the console
            console.log({
                'term': searchTerm,
                'results': searchResults.query.search
            });
        } catch(error) {
            console.log(error);
        }
    }, 500);
};

Поскольку await, связанный с ожиданием, перемещен в функцию обратного вызова setTimeout(), вам необходимо пометить обратный вызов ключевым словом async и удалить ключевое слово async из функции search().

Если вы откроете файл index.html в веб-браузере и наберете ключевое слово debounce без паузы(полсекунды) и остановитесь, вы увидите, что приложение сделает только один запрос к API.

И этот метод известен как Debounce.

Что это такое?

Если у вас есть трудоемкая задача, такая как запрос API, который часто срабатывает, это повлияет на производительность приложения.

Debounce — это метод программирования, который ограничивает количество вызовов функции.

Разработка повторно используемой функции

Функция debounce() должна принимать функцию( fn ), ограничивать количество вызовов к ней и возвращать функцию:

const debounce =(fn) => {
   return(arg) => {
      // logic to limit the number of call fn
      fn(arg);
   };
};

Далее используются функции clearTimeout() и setTimeout() для устранения дребезга функции fn :

const debounce =(fn) => {
    let timeoutId;

    return(arg) => {
        // cancel the previous timer
        if(timeoutId) {
            clearTimeout(timeoutId);
        }
        // setup a new timer
        timeoutId = setTimeout(() => {
            fn(arg);
        }, 500);
    };
};

Обычно функция fn принимает более одного аргумента. Чтобы вызвать функцию fn со списком аргументов, используйте метод apply() :

const debounce =(fn, delay=500) => {
    let timeoutId;

    return(...args) => {
        // cancel the previous timer
        if(timeoutId) {
            clearTimeout(timeoutId);
        }
        // setup a new timer
        timeoutId = setTimeout(() => {
            fn.apply(null, args);
        }, delay);
    };
};

Как это работает:

  • Во-первых, замените жестко заданное число 500 аргументом delay, чтобы можно было указать время ожидания перед выполнением функции fn. Значение задержки по умолчанию составляет 500 мс.
  • Во-вторых, добавьте ...args к возвращаемой функции. ...arg — это остаточный параметр, который позволяет собрать все аргументы функции fn() в массив args.
  • В-третьих, fn.apply(null, args) выполняет функцию fn() с аргументами, указанными в массиве args.

Используйте функцию

Следующий код удаляет логику debounce из функции search() и вместо этого использует функцию debounce() :

const search = debounce(async(searchTerm) => {
    try {
        const url = `https://en.wikipedia.org/w/api.php?

Преобразование результатов поиска в HTML

Мы покажем заголовок и фрагмент каждого результата поиска в выходных данных. Прежде чем сделать это, нам понадобятся некоторые служебные функции:

Удалите HTML-теги

title и snippet из результатов поиска вызова API могут содержать теги HTML. И безопасно удалить все HTML-теги перед их рендерингом.

Следующая служебная функция удаляет теги HTML из строки:

const stripHtml =(html) => {
    let div = document.createElement('div');
    div.textContent = html;
    return div.textContent;
};

Функция stripHtml() принимает строку HTML. Она создает временный элемент <div>, присваивает его innerHTML строку HTML и возвращает его свойство textContent.

Обратите внимание, что эта функция будет работать только в веб-браузерах, поскольку она зависит от API DOM веб-браузера.

Выделите поисковый запрос

Это более интуитивно понятно, если условия поиска выделены в результатах поиска.

Эта функция highlight() выделяет все вхождения keyword в str, заключая каждое вхождение ключевого слова в <span> с классом highlight :

const highlight =(str, keyword, className = "highlight") => {
    const hl = `<span class="${className}">${keyword}</span>`;
    return str.replace(new RegExp(keyword, 'gi'), hl);
};

Обратите внимание, что функция использует регулярное выражение для замены всех вхождений keyword элементом <span>.

Преобразование результатов поиска

Следующая функция generateSearchResultHTML() преобразует результаты поиска в HTML:

Как это работает.

  • Во-первых, используйте метод map() для возврата HTML-представления каждого результата поиска и метод join() для объединения результатов поиска(в формате HTML) в одну строку HTML.
  • Во-вторых, удалите HTML-теги и выделите поисковый запрос в title и snippet, возвращенном из вызова API.

Показ результатов поиска

Измените метод search(), который использует функцию generateSearchResultHTML(), и добавьте его результат в searchResultElem. Кроме того, сбросить результат поиска, если условие поиска пусто:

const search = debounce(async(searchTerm) => {

    // if the search term is removed, 
    // reset the search result
    if(!searchTerm) {
        // reset the search result
        searchResultElem.innerHTML = '';
        return;
    }

    try {
        // make an API request
        const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10&srsearch=${searchTerm}`;
        const response = await fetch(url);
        const searchResults = await response.json();

        // render search result
        const searchResultHtml = generateSearchResultHTML(searchResults.query.search, searchTerm);

        // add the search result to the searchResultElem
        searchResultElem.innerHTML = searchResultHtml;
    } catch(error) {
        console.log(error);
    }
});

Теперь, если вы откроете index.html в веб-браузере, вы увидите работающее приложение.

Заключение

В этом уроке вы узнали следующие ключевые моменты:

  • Используйте API fetch() для отправки HTTP-запросов GET.
  • Используйте ключевые слова async/await, чтобы асинхронный код выглядел чище.
  • Изучите технику программирования Debounce и разработайте повторно используемую функцию JavaScript debounce().
Рейтинг
( Пока оценок нет )
Александр Русаков / автор статьи
Программист, разработчик, 12 лет опыта работы в крупных компаниях. Быстро освоил typescript, делюсь своими знаниями на страницах этого сайта.
Загрузка ...
JavaScript и TypeScript