Как обрабатывать асинхронные ответы через callback и определить лучшую цену
Что проверяют
Обычно это задача на живое кодирование: несколько поставщиков отвечают через колбэки в произвольном порядке, нужно дождаться всех и выбрать минимальную цену. Смотрят, как вы определяете завершение, что делаете с ошибками и зависшими ответами. Следом почти всегда просят переписать то же на промисах.
Как отвечать
Каркас решения на колбэках:
- счётчик завершений: заводите
pending = sources.lengthи уменьшаете его в каждом колбэке, в том числе при ошибке. Проверкаprices.length === nломается, как только один источник упал: массив не наполнится и итог не наступит; - ошибки: в стиле Node колбэк получает
(err, price). Ошибку запоминаете отдельно и ждёте остальных, итог считаете по успешным ответам; - таймаут: общий
setTimeoutзавершает сбор по истечении срока, поздние ответы отсекаются флагомdone. Тот же флаг защищает от поставщика, который вызвал колбэк дважды; - выбор лучшего: храните не только число, но и источник, и сравнивайте на лету:
if (!best || price < best.price) best = { source, price }; - пустой результат: если упали все, итоговый колбэк получает ошибку, а не
Infinity.
Затем покажите современный вариант. Каждый вызов оборачивается в промис вручную или через util.promisify в Node, после чего Promise.allSettled отдаёт все результаты вместе с отказами, и минимум ищется по записям со статусом fulfilled. Таймаут на отдельный запрос в fetch удобно задать через AbortSignal.timeout(ms). Если по условию хватает первого успешного ответа, подходит Promise.any, а Promise.race завершится и на первой ошибке, это разные вещи.
Где спотыкаются
Берут Promise.all. Одна ошибка отклоняет весь результат, и лучшая цена теряется вместе с остальными.
Пишут цикл с var i и читают sources[i] внутри колбэка. К моменту ответа i уже равен длине массива, и ни один ответ не привязан к своему источнику.
Не думают о зависшем поставщике. Без таймаута итоговый колбэк не вызовется никогда, а пользователь будет смотреть на вечный лоадер.