Задача, которую я встретил, может иметь цель не только проверки теоретической подготовки, но есть в ней и практическая ценность.
Есть функция api, которая обращается к адресу и возвращает строку,- токен. Её сигнатуру (контракт) можно описать так:
type Api=(url: string) => Promise<string>;
В задаче нужно обратиться к двум адресам и сравнить строки, если совпадают, то вывести в консоль строку.
Если не совпадают - вывести в консоль строку 'Unauthorized'.
Если один из сервисов не ответил - вывести в консоль строку 'Forbidden'
Если один из сервисов отвечает больше 10 секунд - вывести в консоль строку 'Timeout'.
Для решения этой задачи нам понадобятся два метода работы с промисами: race и all .
И вот для чего нам это нужно.
Определим функцию, которая будет ожидать заданное количество миллисекунд. Эта функция будет выполнять роль таймера- если прошло 10 секунд, то выдать ошибку.
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]);
Вот на этой строке и держится всё решение. Первый элемент массива- это функция-таймер, которая выбрасывает исключение, второй элемент- это запрос токена. Если запрос будет успешным, то промис вернёт его результат; если продлится больше 10 секунд, то сработает исключение с параметром "Timeout".
Эта строка и есть краеугольный камень решения, который имеет практическую ценность. В чём она: Можно ограничить тайм-аут у fetch'a (возможно в практике у Вас такое было: запрос к апи мог длиться больше двух минут и пора бы уже прекращать ожидание ответа).
Аналогично подготовим второй промис:
const request2 = api("ldap-url");
const promise2 = Promise.race([wait(), request2]);
Для выполнения этой части задания "Если один из сервисов не ответил..." нам пригодится Promise.all, который выбрасывает ошибку, если один из сервисов не ответит:
Promise.all([promise1, promise2]).then(
([s1, s2]) => {
const re = s1 === s2 ? s1 : "Unauthorized";
console.log(re);
},
(err) => {
console.error(err);
}
);
Что выше написано: Если строки равны, то вывести первую строку, иначе вывести Unauthorized. Если же какой-то из запросов api будет с ошибкой, то в консоль будет выведена ошибка.
Задача почти готова. Но что-то не хватает. Когда мы посмотрим на текст программы, мы не увидим где задаётся ответ в виде строки "Forbidden". У нас есть вывод в консоль ошибки:
(err) => {
console.error(err);
}
И этот вывод может показать строку "Timeout", но если запрос к api будет ошибочным, то мы получим объект Error. Поэтому давайте промис запроса перепишем:
const promise1 = Promise.race([wait(), request1])
.then((res1) => res1)
.catch((e) => {
throw e === "Timeout" ? "Timeout" : "Forbidden";
});
Итак, мы подошли к решению. Вот его полный текст:
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);
}
);
Top comments (0)