DEV Community

Stanislav Karol
Stanislav Karol

Posted on • Edited on

4 2

Запрос к api с тайм-аутом

Задача, которую я встретил, может иметь цель не только проверки теоретической подготовки, но есть в ней и практическая ценность.

Есть функция api, которая обращается к адресу и возвращает строку,- токен. Её сигнатуру (контракт) можно описать так:

type Api=(url: string) => Promise<string>;
Enter fullscreen mode Exit fullscreen mode

В задаче нужно обратиться к двум адресам и сравнить строки, если совпадают, то вывести в консоль строку.
Если не совпадают - вывести в консоль строку 'Unauthorized'.
Если один из сервисов не ответил - вывести в консоль строку 'Forbidden'
Если один из сервисов отвечает больше 10 секунд - вывести в консоль строку 'Timeout'.

Для решения этой задачи нам понадобятся два метода работы с промисами: race и all .
И вот для чего нам это нужно.
Определим функцию, которая будет ожидать заданное количество миллисекунд. Эта функция будет выполнять роль таймера- если прошло 10 секунд, то выдать ошибку.

function wait(ms = 10000) {
  // Вернуть промис, возвращающий через 10 секунд ошибку:
  // Запуск функции reject c параметром "Timeout"
  return new Promise((resolve, reject) => {
    setTimeout(reject, ms, "Timeout");
  });
}
Enter fullscreen mode Exit fullscreen mode

Далее определим промис для запроса токена:

const request1 = api("oauth-url");
Enter fullscreen mode Exit fullscreen mode

А теперь напишем промис, который будет опрашивать токен с тайм-аутом ожидания:

const promise1 = Promise.race([wait(), request1]);
Enter fullscreen mode Exit fullscreen mode

Вот на этой строке и держится всё решение. Первый элемент массива- это функция-таймер, которая выбрасывает исключение, второй элемент- это запрос токена. Если запрос будет успешным, то промис вернёт его результат; если продлится больше 10 секунд, то сработает исключение с параметром "Timeout".
Эта строка и есть краеугольный камень решения, который имеет практическую ценность. В чём она: Можно ограничить тайм-аут у fetch'a (возможно в практике у Вас такое было: запрос к апи мог длиться больше двух минут и пора бы уже прекращать ожидание ответа).

Аналогично подготовим второй промис:

const request2 = api("ldap-url");
const promise2 = Promise.race([wait(), request2]);
Enter fullscreen mode Exit fullscreen mode

Для выполнения этой части задания "Если один из сервисов не ответил..." нам пригодится Promise.all, который выбрасывает ошибку, если один из сервисов не ответит:

Promise.all([promise1, promise2]).then(
  ([s1, s2]) => {
    const re = s1 === s2 ? s1 : "Unauthorized";
    console.log(re);
  },
  (err) => {
    console.error(err);
  }
);
Enter fullscreen mode Exit fullscreen mode

Что выше написано: Если строки равны, то вывести первую строку, иначе вывести Unauthorized. Если же какой-то из запросов api будет с ошибкой, то в консоль будет выведена ошибка.

Задача почти готова. Но что-то не хватает. Когда мы посмотрим на текст программы, мы не увидим где задаётся ответ в виде строки "Forbidden". У нас есть вывод в консоль ошибки:

  (err) => {
    console.error(err);
  }
Enter fullscreen mode Exit fullscreen mode

И этот вывод может показать строку "Timeout", но если запрос к api будет ошибочным, то мы получим объект Error. Поэтому давайте промис запроса перепишем:

const promise1 = Promise.race([wait(), request1])
  .then((res1) => res1)
  .catch((e) => {
    throw e === "Timeout" ? "Timeout" : "Forbidden";
  });
Enter fullscreen mode Exit fullscreen mode

Итак, мы подошли к решению. Вот его полный текст:

function wait(ms = 10000) {
  // Вернуть промис, возвращающий через 10 секунд ошибку:
  // Запуск функции reject c параметром "Timeout"
  return new Promise((resolve, reject) => {
    setTimeout(reject, ms, "Timeout");
  });
}

const request1 = api("oauth-url");
const promise1 = Promise.race([wait(), request1])
  .then((res1) => res1)
  .catch((e) => {
    throw e === "Timeout" ? "Timeout" : "Forbidden";
  });

const request2 = api("ldap-url");
const promise2 = Promise.race([wait(), request2])
  .then((res2) => res2)
  .catch((e) => {
    throw e === "Timeout" ? "Timeout" : "Forbidden";
  });

Promise.all([promise1, promise2]).then(
  ([s1, s2]) => {
    const re = s1 === s2 ? s1 : "Unauthorized";
    console.log(re);
  },
  (err) => {
    console.error(err);
  }
);
Enter fullscreen mode Exit fullscreen mode

AWS GenAI LIVE image

Real challenges. Real solutions. Real talk.

From technical discussions to philosophical debates, AWS and AWS Partners examine the impact and evolution of gen AI.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay