Ожидание и вычисление требуют разных решений
Чтение файла, обращение к сети и вычисление большого количества значений внешне могут выглядеть как долгое выполнение. Но причины задержки различаются. При асинхронном вводе-выводе среда может организовать ожидание через системные механизмы или вспомогательные ресурсы, оставляя основной поток доступным для другой работы. Подробности зависят от операции; не следует представлять каждое сетевое обращение как отдельный поток JavaScript. Длинный синхронный цикл, напротив, занимает поток, пока не закончится. В это время другие обработчики на том же потоке не получают возможность выполняться. Поэтому задача «посчитать результат» требует другого анализа, чем задача «дождаться ответа». Перед оптимизацией полезно назвать конкретный этап: подготовка данных, ожидание, разбор ответа или собственно расчёт. Общая фраза «всё асинхронное» скрывает эту структуру.
Что делают async, await и цикл событий
Функция с async возвращает обещание результата, но её синхронная часть всё равно выполняет обычную работу на текущем потоке. Если внутри расположен тяжёлый цикл без передачи управления, добавление async не превращает его в фоновое вычисление. await позволяет приостановить продолжение функции до получения результата ожидаемого значения, однако сам по себе не переносит вычисление в другой поток. Цикл событий организует выполнение готовых обработчиков. Это не гарантирует, что каждый обработчик будет быстрым: слишком долгая работа одного задерживает остальных. Даже таймер задаёт не обещание точного момента запуска, а условие, после которого обработчик может получить очередь. Поэтому задержавшийся таймер не обязательно означает неисправность часов; сначала стоит проверить, чем был занят поток и какие задачи ожидали выполнения.
Выбирайте решение после измерения
Для начала измерьте длительность этапов на небольшом воспроизводимом примере. Не запускайте учебную нагрузку на общем рабочем сервере. Если время уходит на ожидание, проверьте корректное использование асинхронного интерфейса и обработку ошибок. Если большую часть занимает вычисление, рассмотрите уменьшение объёма работы, изменение алгоритма или вынесение подходящей задачи в рабочий поток либо отдельный процесс. У каждого способа есть цена: передача данных, память, управление очередью и сложность обработки ошибок. Бесконечный запуск параллельных задач способен перегрузить ресурсы, даже когда основной поток не блокируется. Нужны ограничение одновременной работы и понятное поведение при сбое. Оптимизация считается полезной, когда измерения подтверждают улучшение в нужном сценарии, а корректность и предсказуемость приложения сохраняются.
Попробуйте на практике
Разберите модель обработки запроса на бумаге, не создавая нагрузку на действующий сервис.
- Запишите четыре этапа: проверка входных данных, ожидание файла, разбор содержимого и вычисление результата.
- Для каждого этапа укажите, где вероятно ожидание, а где работа процессора. Отдельно отметьте неизвестные детали реализации.
- Предположите, что вычисление выполняется длинным синхронным циклом внутри async-функции. Объясните, почему другие обработчики могут задержаться.
- Предложите два способа уменьшить задержку и для каждого назовите измерение, возможную цену и правило обработки ошибки.
Как проверить результат. В объяснении должны различаться ожидание ввода-вывода, выполнение JavaScript и перенос вычислений. Слова async и await не должны использоваться как синонимы отдельного потока или гарантированной скорости.
Частые вопросы
Значит ли это, что в Node.js всегда только один поток?
Нет. По умолчанию основной JavaScript выполняется на одном потоке, но среда использует дополнительные механизмы, а рабочие потоки можно создавать явно. Важно уточнять, о какой части выполнения идёт речь.
Нужно ли переносить любое вычисление в рабочий поток?
Нет. Для коротких задач расходы на организацию и передачу данных могут оказаться неоправданными. Сначала измеряют проблему и проверяют простые изменения алгоритма и объёма входных данных.
Самостоятельный разбор темы. Содержание конкретной обучающей программы здесь не представлено.