Задать вопрос
@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());

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

Задача вот какая, у меня много запросов при загрузке страницы, там есть проверка токена, и если проверка не пройдена на первом запросе, мне нужно чтобы следующие запросы не отправлялись. На данный момент они уходят все.
  • Вопрос задан
  • 316 просмотров
Подписаться 1 Простой Комментировать
Решения вопроса 3
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.
Так же выставляется флаг, мол в процессе обновления токена и все последующие запросы помещаются в массив промисов.
Далее в цикле проверяем, можно ли разрешить промис если да то выдыхаем и запросы ушли на сервер.
Ответ написан
IonDen
@IonDen
JavaScript developer. IonDen.com
Идея в том чтобы разбить выполнение кода на 2 шага.
В первом шаге делаем только запрос на проверку токена.
Во втрором шаге все остальное.

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

Делать запросы синхронными (в прямом смысле) конечно же не нужно.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

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

Похожие вопросы