В этом руководстве вы узнаете о функции debounce JavaScript, которая ограничивает количество вызовов функции, и о том, как использовать ее для повышения производительности приложения.
Чтобы понять функцию debounce, мы создадим приложение для поиска в Википедии, используя метод программирования debounce в JavaScript.
- Создайте структуру папок проекта
- Создайте HTML-страницу
- Скопируйте код CSS
- Обработка входных событий
- Получение результатов поиска с помощью API Википедии
- Что это такое?
- Разработка повторно используемой функции
- Используйте функцию
- Преобразование результатов поиска в HTML
- Удалите HTML-теги
- Выделите поисковый запрос
- Преобразование результатов поиска
- Показ результатов поиска
- Заключение
Создайте структуру папок проекта
Сначала создайте новую папку с именем wikipedia-search
, в которой будут храниться файлы проектов.
Во-вторых, создайте три папки внутри папки wikipedia-search
с js
, css
и img
. В этих папках будут храниться файлы JavaScript, CSS и изображений соответственно.
В-третьих, создайте style.css
в папке css
и app.js
в папке js
. Также загрузите следующее изображение и скопируйте его в папку img
. Вы будете использовать логотип для создания пользовательского интерфейса приложения.
Наконец, создайте файл 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()
.