Jeremy D Richardson Ответов: 1

Лучшее решение? - Обновление API на предъявителя маркер с несколько асинхронных вызовов JavaScript, что позволяет использовать этот маркер


У меня есть проблема, которая, как мне кажется, должна быть решена каким-то элегантным способом, но мне нужен какой-то вклад. Я использую ванильный Javascript для решения этой проблемы, но мне интересно, есть ли более элегантное решение React или какая-то другая библиотека.

Проблема: У вас есть API, который требует токена на предъявителя, который длится всего 15 минут. Каждый раз, когда делается запрос на данные, мы должны проверить, является ли токен действительным, а если нет, то запросить новый токен. Проблема в том, что между запросом и получением нового токена проходит некоторое время. Тем временем можно было бы сделать больше запросов на данные, что привело бы к еще одному ненужному запросу на новый токен.

const api = new testApi();
api.getSomeData(1);
api.getSomeData(2);
api.getSomeData(3);


Очевидно, что это очень упрощенный тест для демонстрации.

Происходит первый вызов getSomeData. Поскольку исходного токена не было, он запрашивает новый, а затем возвращает свои данные. Однако из-за асинхронной природы Javascript вторые 2 вызова getSomeData срабатывают до того, как первый getSomeData получил новый токен. Поэтому 2 последующих вызова getSomeData также запрашивают новый токен.

Что я уже пробовал:

Вот полный класс, который я создал, который, как я считаю, решает эту проблему (но все еще задаюсь вопросом, является ли это лучшим решением):

class testApi {
  constructor() {
    this.token = "";
  }

  // Return a promise with the resulting token
  getToken() {
    this.token = new Promise((resolve, respond) => {
      setTimeout(() => {
        console.log("Got a new token!");
        resolve(Math.random() * 1000);
      }, 1000);
    });
  }

  // Simple check to determine if token is valid
  isValidToken() {
    if (this.token === "") return false;
    return true;
  }

  // Call to api to get some data
  getSomeData(callNum) {
    if (!this.isValidToken()) {
      console.log("Token not valid so we need to get another");
      this.getToken();
    }

    // Token is always a promise so we use .then to make sure it's current
    this.token.then((result) =>
      console.log(`Token for call ${callNum} is ${result}`)
    );
  }
}

const api = new testApi();
api.getSomeData(1);
api.getSomeData(2);
api.getSomeData(3);


Установив this.token на обещание, которое возвращается запросом на новый токен (моделируемый здесь setTimout), любой запрос на этот токен должен использовать .затем, чтобы получить действительный токен.

В getSomeData действительный токен появляется только в обратном вызове этого.token.затем(). Если бы мы попытались получить доступ к этому токену за пределами .тогда он вернет невыполненное обещание.

Результатом работы этого кода является:

Token not valid so we need to get another
Got a new token!
Token for call 1 is 897.2653861706979
Token for call 2 is 897.2653861706979
Token for call 3 is 897.2653861706979


Есть мысли о лучшей методологии для этого?

Спасибо!

1 Ответов

Рейтинг:
1

Chris Copeland

Я не особенно разбираюсь в современном JS и доступных ему классах, но если вас беспокоят асинхронные веб-запросы, обновляющие токен одновременно, вам придется заглянуть во что-то вроде мьютекса. Для потока операций я бы ожидал чего-то вроде:


  • Проверьте, действителен ли токен (false)
  • Получить мьютекс
  • Проверьте, является ли токен все еще недействительным (другая асинхронная операция могла бы изменить это)
  • Обновить токен
  • Освободить мьютекс
  • Приступать

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

С точки зрения вашего кода, я ожидаю, что вы замените token с новым Promise, которые затем могут быть использованы с помощью then() метод.