Как по-настоящему сделать трудоемкий запрос асинхронным в узле

Я пытаюсь масштабировать свое приложение nodejs, чтобы оно могло эффективно обрабатывать несколько запросов одновременно. Одним из способов достижения этого является оптимизация кода, и я хотел знать, как я могу сделать трудоемкие запросы асинхронными, являются ли обещания единственным способом? или есть другой механизм для достижения того же?

Я уже использовал промисы для асинхронных задач, но, насколько я понимаю промисы, действительно асинхронная часть — это обработчики then и catch. Когда кто-то создает обещание, функция-исполнитель будет выполняться основным потоком до тех пор, пока он не выполнит какую-либо асинхронную задачу (setTimeout и т. д.).

Пример кода отправки письма

   app.route('/api/contact/sendEmail')
       .post((req, res) =>{
           sendEmail(req).then( () =>{
               res.status(200);
               res.json({
                   sent: true
               });
           }).catch(emailError=> {
               //Custom error send bad request.
               if (emailError.code === 900) {
                   res.status(400);
                   res.json({
                       errorCode: emailError.code,
                       message: emailError.message
                   });
               }else {
                   res.status(500);
                   res.json({
                       errorCode: emailError.code
                   });
               }
           });
       });

Поток не блокируется для отправки ответа, но до тех пор, пока sendEmail не достигнет фактической асинхронной части, основной поток будет заблокирован.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
364
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Немного сложно ответить на ваш вопрос, поскольку вы не даете подробной информации о библиотеке/функциях, которые вы используете, например. откуда берется функция sendMail, что такое app и в какой момент вы думаете, что что-то блокирует.

Однако, как правило, ваш код в Node.JS всегда выполняется в основном потоке. Это означает, что каждая строка вашего файла .js находится в основном потоке, независимо от того, является ли что-либо «асинхронным» или нет.

Теперь, чтобы действительно позволить чему-то быть «асинхронным» (то есть: когда выполняется пока другой код, выполняется другой код), должен быть способ засунуть материал в другой поток для выполнения. И это то, что на самом деле происходит в Node.JS под капотом. Node.JS использует libuv/libev для реализации цикла событий для обработки задач. Он также имеет ряд фоновых/рабочих потоков (по умолчанию их 4), которые выполняют блокирующие задачи, например сетевой или файловый ввод-вывод.

Однако это абстрагируется от разработчика Node.JS, поскольку скрыто в реализации соответствующего модуль. Например, встроенная файловая система или сетевые модули будут выполнять свои задачи в фоновом потоке (если только вы не используете один из вариантов функции -sync). Если вы используете сторонний модуль, это зависит от того, написан ли он на C/C++ и использует libuv для доступа к рабочим потокам и выполнения фоновой обработки. Если это не так, то ваш сторонний модуль будет выполнять код в основном потоке так же, как и ваш код, если это так, то выполнение действительно выполняется "асинхронно"/в фоновом потоке.

Также см. эту очень информативную ветку: Как работает однопоточная неблокирующая модель ввода-вывода в Node.js

Как это вам поможет?
В основном я хотел сказать вам, что все зависит от функций/модулей, которые вы используете, и от того, реализованы ли они как фоновая операция (с использованием libuv) или нет. Кроме того, вы не можете сделать что-либо асинхронным/синхронным самостоятельно, вы обязаны использовать все, что реализует функция/модуль.

Для полноты картины я хотел бы упомянуть, что Node.JS 11 представил модуль worker_threads, который позволяет использовать потоки в коде javascript, что позволяет вам помещать что-то в фоновый поток и не блокировать основной поток. Обратите внимание, что стабильность модуля пока "экспериментальная".

Примечание
Вы действительно уверены, что ваш код блокировка основного потока? Поскольку, как упоминалось ранее, сетевой ввод-вывод в любом случае происходит в фоновых потоках, поэтому маловероятно, что sendMail блокирует основной поток (при условии, что работа sendMail заключается в выполнении сетевого ввода-вывода). Как вы узнали, что он блокируется?

Итак, если мне нужно сделать что-то неблокирующее с помощью моего кода, и если модуль или функция, которые я использую, не используют фоновые потоки, тогда лучше всего будет придерживаться только обещаний? app=express() Для примечания я пропустил добавление того, что sendEmail использует nodemailer. Таким образом, фактическая часть отправки электронной почты, как я полагаю, не блокируется, но до тех пор, пока управление не достигнет фактической отправки электронной почты, мой основной поток останется заблокированным. Спасибо за подробный пост о libuv/libve, проверим статью, которой вы поделились, а также документацию экспериментального модуля.

Saurav Seth 12.02.2019 09:13

Промис просто предоставляет другой API для того же самого. Нет разницы между асинхронным и неасинхронным. Он просто позволяет писать код в стиле bla.then().then().then().catch() и так далее, вместо bla(function callback(...) { moreStuff(function anotherCallback(...) { } }); });. То же самое для ключевых слов async/await.

user826955 12.02.2019 10:30

Что касается вашего пункта «но пока контроль не дойдет до фактической отправки электронного письма, моя основная ветка останется заблокированной», я не совсем уверен, что понимаю вас. Размещенный вами код подключает маршрутизатор к вашему app и прикрепляет к нему обработчик HTTP-запросов POST. Этот обработчик (функция с сигнатурой (req,res) => ()) вызывается каждый раз, когда вы получаете HTTP-запрос. Нет части, на которую можно было бы ссылаться с помощью «пока управление не достигнет фактической отправки», потому что первое выражение в вашем обработчике — sendMail.

user826955 12.02.2019 10:33

Другими словами: app.route().post() не блокирует, он настраивает вашу маршрутизацию, а потом возвращается. Функция обратного вызова, которую вы предоставляете (req,res) => (), вызывается для каждого запроса, как только он достигает вашего приложения. Где вы видите реальный блокировка?

user826955 12.02.2019 10:34

Насколько я понимаю, пока sendEmail фактически не начнет выполнять ввод-вывод, основной поток будет заблокирован. Таким образом, почтовый маршрут не будет блокироваться, но фактическое выполнение упомянутого вами обратного вызова, который вызывает sendEmail, который вызывает API отправки nodemailer, будет блокироваться до тех пор, пока nodemailer не вернет обещание (возвращается после того, как он начал асинхронную отправку электронной почты). Обещания будут задерживать обработчики then и catch, добавляя их в конец очереди, в отличие от обратных вызовов, которые начнут выполняться сразу после вызова и, таким образом, заблокируются, верно?

Saurav Seth 12.02.2019 11:41

В этом случае это будет проблема почтовой библиотеки, однако я считаю маловероятным, что она потратит много времени на «ничегонеделание», а затем вернется после запуска ввода-вывода. Есть ли какой-либо больший контент, который вы пытаетесь отправить (мегабайты, гигабайты), который просто требует времени для копирования в рабочий поток? В любом случае я не понимаю, как вы могли бы решить это на своей стороне. Вы добавили сообщения журнала отладки, чтобы узнать, возвращается ли sendMail немедленно (с обещанием) или только через некоторое время?

user826955 12.02.2019 11:49

Нет. Это не блокирует ваши мысли. Я хотел добавить пару вещей, которые заняли бы некоторое время (это все равно не более одной секунды), но я думал с точки зрения, скажем, я получаю 200 запросов в одну секунду, тогда, если я добавлю что-то дополнительное, что займет около секунды, то обслуживание этих многочисленных запросов будет проблематичным - я понимаю, что для обработки такого количества запросов серверная машина также должна быть масштабирована. Это была скорее теоретическая проблема, с которой я могу столкнуться или не столкнуться.

Saurav Seth 12.02.2019 11:56

Давайте продолжить обсуждение в чате.

Saurav Seth 12.02.2019 12:05

Другие вопросы по теме