Как реализовать функцию delay с задержкой и возвратом результата?
Что проверяют
Умение обернуть колбэчный API в промис: где создаётся Promise, что попадает в resolve, почему функция возвращает промис, а не результат. Дальше обычно идёт продолжение про отмену и про то, гарантирует ли setTimeout точное время.
Как отвечать
Базовая версия занимает одну строку, потому что setTimeout умеет прокидывать дополнительные аргументы в колбэк:
function delay(ms, result) {
return new Promise((resolve) => setTimeout(resolve, ms, result));
}
await delay(2000, "Hello"); // через 2 секунды вернёт "Hello"
await delay(1000); // просто пауза, результат undefinedРазвёрнутый эквивалент, если хочется явности:
function delay(ms, result) {
return new Promise((resolve) => {
setTimeout(() => resolve(result), ms);
});
}Что стоит добавить голосом:
msэто минимальная задержка, а не точная. Колбэк ставится в очередь макрозадач и выполнится не раньше срока, но позже, если движок занят. Вложенные таймеры глубже пятого уровня браузер прижимает к 4 мс, а в фоновой вкладке интервал урезается до одного раза в секунду и больше.- Отмена. Промис отменить нельзя, но таймер можно снять, поэтому боевая версия принимает
AbortSignal.
function delay(ms, result, { signal } = {}) {
return new Promise((resolve, reject) => {
if (signal?.aborted) return reject(signal.reason);
const id = setTimeout(() => resolve(result), ms);
signal?.addEventListener(
"abort",
() => {
clearTimeout(id);
reject(signal.reason);
},
{ once: true }
);
});
}- В Node то же самое есть из коробки:
import { setTimeout as delay } from "node:timers/promises", сигнатураdelay(ms, value, { signal }). Плюс.unref()у таймера, если пауза не должна держать процесс живым. - Если результат дорогой, разумнее принимать функцию и звать её в момент срабатывания:
resolve(typeof result === "function" ? result() : result), иначе значение вычисляется до начала паузы.
Типовое применение: пауза между ретраями с экспоненциальным ростом, троттлинг запросов к внешнему API, искусственная задержка в тестах.
Где спотыкаются
Пишут delay без return перед new Promise и потом не понимают, почему await отрабатывает мгновенно. Второе: оставляют таймер незакрытым при отмене операции, и в тестах процесс не завершается, пока не отработают все висящие setTimeout.