Почему JS однопоточный, но неблокирующий? Promise.all и race
Как один поток справляется с асинхронностью и какие комбинаторы промисов спросят.
JavaScript выполняет код в одном потоке, но долгие операции (сеть, таймеры, файлы) делегирует среде (браузеру/Node), а результаты обрабатывает через очередь — поэтому он не «висит».
Один поток, но без блокировки
В JS только один call stack — в каждый момент выполняется одна строка кода. Но операции ввода-вывода выполняет не сам движок, а окружение. Пока «грузится» запрос, движок свободен и делает другую работу; когда результат готов, его коллбэк попадает в очередь.
Поэтому тяжёлый синхронный код блокирует всё (зависает интерфейс), а асинхронные операции — нет.
console.log("старт");
// синхронный тяжёлый цикл блокирует поток
let sum = 0;
for (let i = 0; i < 1000000; i++) sum += i;
console.log("посчитали:", sum);
console.log("конец");
Вывод:
старт посчитали: 499999500000 конец
Promise.all — ждём все
Promise.all ждёт, пока выполнятся все промисы, и возвращает массив результатов. Если хоть один упал — весь all падает.
const p1 = Promise.resolve("user");
const p2 = Promise.resolve("orders");
const p3 = Promise.resolve("settings");
Promise.all([p1, p2, p3]).then((results) => {
console.log(results);
});
Вывод:
[ 'user', 'orders', 'settings' ]
Promise.race — кто первый
Promise.race возвращает результат первого завершившегося промиса (успех или ошибка). Удобно для таймаутов.
const fast = Promise.resolve("быстрый");
const slow = new Promise((res) => setTimeout(() => res("медленный"), 50));
Promise.race([fast, slow]).then((winner) => {
console.log("победил:", winner);
});
Вывод:
победил: быстрый
allSettled и any
Promise.allSettled дожидается всех и возвращает статусы (не падает от одной ошибки). Promise.any возвращает первый успешный.
const ok = Promise.resolve("успех");
const fail = Promise.reject("ошибка");
Promise.allSettled([ok, fail]).then((res) => {
console.log(res.map((r) => r.status));
});
Promise.any([fail, ok]).then((v) => console.log("any:", v));
Вывод:
[ 'fulfilled', 'rejected' ] any: успех
Памятка по комбинаторам
| Метод | Когда разрешается |
all | все успешны (иначе падает на первой ошибке) |
race | первый завершившийся (успех ИЛИ ошибка) |
allSettled | все завершились, со статусами |
any | первый успешный |
Итог
- JS однопоточный: тяжёлый синхронный код блокирует, асинхронный — нет.
allждёт всех и падает от одной ошибки;race— первый завершившийся.allSettledвернёт статусы всех;any— первый успешный.