Как стать автором
Обновить

AbortController для отмены запросов при смене страницы (SPA)

Уровень сложности Средний
Время на прочтение 3 мин
Количество просмотров 2.9K
Разработка веб-сайтов *JavaScript *Программирование *Клиентская оптимизация *VueJS *
Туториал

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

Особенно неприятно это становится тогда, когда у пользователя медленный интернет и каждый лишний запрос еще больше замедляет работу вебсайта. К слову, про оптимизацию 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() }])
    ...
  } catch ({ response }) {
    ...
  }
}

У нас проект на Vue.js, поэтому далее будет пример кода именно для него.

В файле роутера воспользуемся хуком beforeEach, который будет срабатывать при каждой смене страницы, в нем нам нужно будет отменить запросы, а также заново проинициализировать контроллер:

// router.js

import { abortController, reinitController } from '@/services/abortController'

...

router.beforeEach(() => {
  abortController()
  reinitController()
})

Вот и все! Теперь все незаконченные (pending) запросы будут прерываться при смене страницы, не вызывая ненужных ошибок и не используя зря сеть пользователей сайта с плохим интернетом.

Выглядеть эти запросы будут вот так, со статусом canceled:

Теги:
Хабы:
Всего голосов 5: ↑4 и ↓1 +3
Комментарии 8
Комментарии Комментарии 8

Публикации

Истории

Работа