Кэширование вызовов API в JavaScript путем запоминания промисов

Кэширование вызовов API в JavaScript путем запоминания промисов

14 марта 2022 г.

Кэширование вызовов API — это распространенный способ повысить производительность веб-приложений, поскольку кэширование сокращает количество ненужных и избыточных вызовов API. Вот некоторые распространенные сценарии, в которых могут происходить ненужные вызовы API:


  1. Несколько модулей в вашем приложении, которые используют одну и ту же конечную точку API в разные моменты времени.

  1. Тот же самый API вызывается снова, когда пользователь повторно посещает страницу.

Если какой-либо из повторных вызовов каждый раз предоставляет одни и те же данные, мы можем избежать их путем кэширования, а также сохранить круговые поездки. Это уменьшит нагрузку на сервер и одновременно повысит производительность вашего веб-приложения.


Как кэшировать вызов API?


HTTP-кэш браузера должен быть первым выбором для кэширования вызовов API, поскольку он эффективен, поддерживается во всех браузерах и прост в реализации. Кэширование вызова API в кэше HTTP браузера управляется его заголовками запроса и заголовками ответа.


Вот хорошая статья о настройке кэша HTTP на стороне клиента и сервера - https://web.dev/http-cache/


Для более глубокого понимания HTTP-кэширования ознакомьтесь со статьей MDN о HTTP-кэшировании — https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching.


В заключение следует отметить, что HTTP-кэширование в браузере работает в сочетании как клиентских, так и серверных конфигураций . Поэтому нам нужно контролировать как веб-приложение, так и веб-сервер для настройки заголовков запросов и ответов, чтобы кэширование HTTP работало в соответствии с нашими требованиями. Но иногда у нас может не быть контроля над конфигурациями на стороне сервера, или сервер может отправлять не все необходимые заголовки кеша в ответе.


В этой статье давайте обсудим один из способов кэширования вызовов API в веб-приложении с помощью JavaScript, если HTTP-кеш у вас не работает.


Кэширование вызовов API путем запоминания промисов


В вычислениях мемоизация или мемоизация - это метод оптимизации, используемый в основном для ускорения компьютерных программ путем сохранения результатов дорогостоящих вызовов функций и возврата кэшированного результата при повторении тех же входных данных. Это [Википедия] (https://en.wikipedia.org/wiki/Memoization#:\~:text=In%20computing%2C%20memoization%20or%20memoisation,%20то же%20inputs%20occur%20снова) определение мемоизации, и мы собираемся использовать эту технику для кэширования наших вызовов API.


В случае вызовов API результатом будет обещание, а обещания являются объектами javascript, поэтому мы может хранить его в структуре данных map (называя ее `cache`) с уникальным ключом для каждого запроса. Если тот же API снова вызывается с тем же ключом, мы можем вернуть промис из кеша, не загружая его снова с сервера.


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


```javascript


const memoizePromiseFn = (fn) => {


константный кеш = новая карта();


возврат (...аргументы) => {


константный ключ = JSON.stringify(аргументы);


если (кэш.имеет(ключ)) {


вернуть cache.get (ключ);


cache.set(ключ, fn(...args).catch((ошибка) => {


// Удалить запись из кеша, если вызов API не удался


cache.delete(ключ);


вернуть Promise.reject (ошибка);


вернуть cache.get (ключ);


Входными данными для этой функции будет наша служебная функция, которая выполняет вызов API на сервер и возвращает результат. Например,


```javascript


функция fetchTodo(id) {


вернуть выборку(https://jsonplaceholder.typicode.com/todos/${id})


.then((ответ) => response.json())


.then((json) => json);


Мы можем кэшировать вызовы вышеуказанной функции с параметром id в качестве ключа, используя нашу функцию promiseMemoize. Таким образом, если функция fetchTodo вызывается с тем же id во второй раз, ответ будет обслуживаться из кеша без обращения к серверу. В случае сбоя вызова API кэшированное значение для конкретного ключа будет очищено.


```javascript


асинхронная функция getTodos() {


// Здесь вызывается функция более высокого порядка


let cachedFetchTodos = memoizePromiseFn(fetchTodo);


let response1 = await cachedFetchTodos(1); // Обращение к серверу с идентификатором 1


let response2 = await cachedFetchTodos(2); // Обращение к серверу с идентификатором 2


let response3 = await cachedFetchTodos(1); // id равен 1, будет обслуживаться из кеша


let response4 = await cachedFetchTodos(3); // Обращение к серверу с идентификатором 3


let response5 = await cachedFetchTodos(2); // id равен 2, будет обслуживаться из кеша


// Общее количество вызовов - 3


getTodos();


Вы можете просмотреть демо в этом codesandbox


Репозиторий Github — https://github.com/aldrinpvincent/memoize-promise-fn


Если вы используете реакцию, поместите вызов memoizePromiseFn, т.е.


let cachedFetchTodos = memoizePromiseFn(fetchTodo);


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


Каковы проблемы с этим подходом к кэшированию?


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


Кроме того, это неправильный механизм кэширования, так как кэш очищается при перезагрузке страницы. Этот механизм может работать для одностраничного приложения, где маршрутизация происходит на стороне клиента без полной перезагрузки страницы, и применим только для API. вызовы, ответ на которые не будет меняться часто.



Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE