Существует довольно распространенная проблема, когда пользователь заходит на страницу, для этой страницы начинают запрашиваться различные данные, но вдруг пользователь быстро переходит на другую страницу, а запросы с предыдущей страницы продолжают выполняться, все еще используя ресурсы сети и выполняя ненужную работу.
Особенно неприятно это становится тогда, когда у пользователя медленный интернет и каждый лишний запрос еще больше замедляет работу вебсайта. К слову, про оптимизацию SPA я писал в этой статье, а эту статью можно использовать и как небольшое дополнение и еще один способ оптимизации для пользователей с плохим интернетом.
А иногда это приводит и к ошибкам. Например, у нас есть две страницы — страница соревнований и страница турниров, обе страницы имеют id в URL и данные запрашиваются по этому id. Когда пользователь быстро переходит со страницы турнира на страницу соревнования, то id турнира может подмениться на id соревнования, и тогда получится, что запрос пойдет на несуществующий для турнира id и упадет ошибка.
Когда я столкнулся с этой проблемой, то первой мыслью было использовать AbortController для отмены таких “висящих” запросов, и я решил загуглить как это правильно сделать, но сразу же был крайне удивлен тем, что советы на stackoverflow или различные статьи как-то очень странно и скудно освещали эту проблему, иногда предлагая абсолютно нерабочие решения, поэтому и решено было написать эту статью, чтобы показать конкретный рабочий пример.
Для тех, кто с этим не знаком, AbortController — это, простыми словами, интерфейс, который позволяет управлять отменой http запросов со стороны фронтенда.
Итак, для начала нам нужно создать сервис, который бы создавал экземпляр нашего контроллера и экспортировал методы для управления им:
// abortController.js let controller = new AbortController() export const getControllerSignal = () => controller.signal export const abortController = () => controller.abort() export const reinitController = () => { controller = new AbortController() }
Пройдемся по всему по порядку:
-
signalнам нужен для передачи его в запрос, чтобы в дальнейшем мы могли этим запросом управлять и отменять его -
метод
abort()нужен собственно для отмены запроса, в который мы передалиsignal -
и важный момент — для того чтобы запросы прерывались не навсегда после первого раза, а только в нужный момент, после его отмены нам нужно заново проинициализировать наш контроллер, для этого и нужен метод
reinitController, который просто вызываетnew AbortController()
Теперь нам нужно передавать signal во все наши запросы, чтобы мы могли их отменять. К счастью, на нашем проекте есть функция, которая управляет всеми запросами, так что нам не придется прокидывать signal в каждый запрос по отдельности:
// apiHelper.js import { getControllerSignal } from '@/services/abortController' export const callPrivateApi = async(method, url, ...args) => { try { const { data } = await privateApi[method](url, ...[...args, { signal: getControllerSignal() }]) return { data, error: null } } catch ({ response }) { return { data: null, error: { text: response?.data, status: response?.status, }, } } }
У нас проект на Vue.js, поэтому далее будет пример кода именно для него.
В файле роутера воспользуемся хуком beforeEach, который будет срабатывать при каждой смене страницы, в нем нам нужно будет отменить запросы, а также заново проинициализировать контроллер:
// router.js import { abortController, reinitController } from '@/services/abortController' ... router.beforeEach(() => { abortController() reinitController() })
Вот и все! Теперь все незаконченные (pending) запросы будут прерываться при смене страницы, не вызывая ненужных ошибок и не используя зря сеть пользователей вебсайта.
ссылка на оригинал статьи https://habr.com/ru/articles/746740/
Добавить комментарий