@lookingfor2

Как сделать запросы к api синхронными?

Есть функция, которая вызывается при загрузке страницы
getData() {

//тут запросы к api
      const arrayApi = [
        { name: 'data1', apiFunction: apiGetData1 },
        { name: 'data2', apiFunction: apiGetData2 },
        { name: 'data3', apiFunction: apiGetData3 },
      ];
//достаем из объекта выше запросы
const requests = arrayApi.filter(item => item.apiFunction());
//создаем из них промисы ответ отправляем в функцию packAnswer
   Promise.all(requests)
        .then((responses) => {
          responses.forEach(
            response => packAnswer(response),
          );
        });
}

Все дело в том, что arrayApi.apiFunction вызывает другую функцию из другого файла
Например возьмем первое обращение к Апи(apiGetData1) из массива обращений(arrayApi),
export const apiGetData1 = () => api('data', 'get', getAccessToken());

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

Задача вот какая, у меня много запросов при загрузке страницы, там есть проверка токена, и если проверка не пройдена на первом запросе, мне нужно чтобы следующие запросы не отправлялись. На данный момент они уходят все.
  • Вопрос задан
  • 123 просмотра
Решения вопроса 3
IonDen
@IonDen
JavaScript developer. IonDen.com
Идея в том чтобы разбить выполнение кода на 2 шага.
В первом шаге делаем только запрос на проверку токена.
Во втрором шаге все остальное.

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

Делать запросы синхронными (в прямом смысле) конечно же не нужно.
Ответ написан
IgorPI
@IgorPI
Недавно такая же проблема была.
Всё тот же await, но только конкретная реализация.

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

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

spoiler

import { app } from '@/main'
import { Cookie } from '@/plugins/cookie'
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import Vue from 'vue'
import { sleep } from '@/Utils'

// Full config:  https://github.com/axios/axios#request-config
// axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

/* eslint-disable */
const config = {
  baseURL: process.env.VUE_APP_API,
  timeout: 30000,
  validateStatus (status: number) {
    return status < 500 // Resolve only if the status code is less than 500
  }
}
/* eslint-enable */

const _axios: AxiosInstance = axios.create(config)
const cookie: Cookie = new Cookie()
let isRefreshTokenProcess = false
const promises: any[] = []
/* eslint-disable */
// @ts-ignore
_axios.interceptors.request.use(async (config: AxiosRequestConfig): AxiosRequestConfig | Promise<AxiosRequestConfig> => {

  // todo: set locale optional
  config.headers.Language = 'ru'

  if (isRefreshTokenProcess) {
    console.log('%c%s', 'color: red;', `Запрос ${config.url} ожидает обновление токена...`)
    promises.push(new Promise(async (resolve) => {
      while (isRefreshTokenProcess) {
        await sleep(500)
      }
      console.log('%c%s', 'color: green;', `Запрос ${config.url} разрешён!`)
      resolve()
    }))
    // This is process update token
    await Promise.all(promises)
  }

  if (cookie.has('access_token')) {
      config.headers.Authorization = `Bearer ${cookie.get('access_token')}`
      return config
    } else {
      if (cookie.has('refresh_token')) {
        console.log('%c%s', 'color: blue;', 'Обновление токена...')
        isRefreshTokenProcess = true
        await axios.post(`${process.env.VUE_APP_API}/account/authorization/refresh-token`, {
          refresh_token: cookie.get('refresh_token')
        }).then((response: AxiosResponse) => {
          if (response.status === 200) {
            cookie.set('access_token', response.data.access_token, { 'max-age': 600, 'path': '/' })
            cookie.set('refresh_token', response.data.refresh_token, { path: '/' })
            config.headers.Authorization = `Bearer ${cookie.get('access_token')}`
          }
        }).catch(() => {
          app.$router.replace({ name: 'login' }).then()
        })
      }
      isRefreshTokenProcess = false
      return Promise.resolve(config)
    }
  },
  function (error) {
    // Do something with request error
    return Promise.reject(error)
  }
)
/* eslint-disable */
/*
  Array of patterns to ignore status checks.
 */

// Add a response interceptor
_axios.interceptors.response.use(
  (response): Promise<AxiosResponse> | any => {
    if (response.status === 401) {
      return app.$router.replace({ name: 'login' }).finally(() => Promise.reject(response))
    } else {
      return response
    }
  },
  function (error) {
    // Do something with response error
    return Promise.reject(error)
  }
)

class AxiosPlugin {
  public install () {
    Object.defineProperties(Vue.prototype, {
      axios: {
        get () {
          return _axios
        }
      },
      $axios: {
        get () {
          return _axios
        }
      }
    })
  }
}

const axiosPlugin: AxiosPlugin = new AxiosPlugin()

Vue.use(axiosPlugin)

export default axiosPlugin
export const $axios: AxiosInstance = _axios



Работает следующим образом.
Токен хранится в куках, с меньшим временем хранения, то есть с куков будет удалён раньше чем протухнет на сервере.

Перед выполнением запроса, мы проверяем, есть ли токен в куках, если нет то обновляем с помощью refresh_token.
Так же выставляется флаг, мол в процессе обновления токена и все последующие запросы помещаются в массив промисов.
Далее в цикле проверяем, можно ли разрешить промис если да то выдыхаем и запросы ушли на сервер.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы