#javascript #node.js #express #async-await #nonblocking
Вопрос:
Мы все знаем, что NodeJS однопоточен, что означает, что если в нашем коде есть операция async/await, узел будет ждать, пока она будет выполнена, прежде чем выполнять остальную часть кода. Поэтому, если пользователь делает асинхронный запрос, другие пользователи должны подождать, пока это будет сделано, прежде чем делать запросы тоже?
Здесь я создал простой пример, первый маршрут использует асинхронную функцию, и для отправки ответа требуется 10 секунд, а второй маршрут отправляет ответ немедленно.
Когда я отправил запрос на первый маршрут и в ожидании ответа отправил еще один запрос на второй маршрут, и я получил ответ, хотя первый маршрут еще не завершил выполнение кода.
Почему в этом примере он не блокируется?
function sleep(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(true)
},10000)
}).then(val=>val)
}
router.get('/route1',async (req,res)=>{
const test = await sleep()
res.send('HELLO WORLD')
})
router.get('/route2',(req,res)=>{
res.send("HELLO WORLD")
})
Комментарии:
1.
await
не блокирует поток.await
это синтаксический сахар, который преобразует вашуasync
функцию в обратные вызовы во время выполнения2.
sleep
Функция должна возвращать обещание. Вам не нужно добавлять athen
, потому что Обещание решается вызовомawait
.3. кроме того, http является асинхронным, поэтому отправка 2 разных запросов-это 2 разных запроса
Ответ №1:
await
блокирует/приостанавливает выполнение только текущей функции, а не всего интерпретатора. Фактически, в тот момент, когда функция попадает в первую await
внутри функции, функция немедленно возвращает обещание, и другая обработка после того, как эта функция (или другие происходящие события) будут свободны для запуска.
Итак, в вашем примере, когда он попадает в await sleep()
, выполнение этой функции приостанавливается до тех await
пор, пока функция не разрешит/не отклонит, и содержащая async
функция немедленно вернет невыполненное обещание. Поскольку Express with router.get()
ничего не делает с этим возвращенным обещанием, он просто игнорирует его и возвращает управление обратно в цикл событий. Некоторое время спустя ваш второй запрос поступает на сервер, событие помещается в очередь событий nodejs, и Express вызывается с этим событием, и оно обслуживает ваш второй обработчик маршрута.
поэтому, если пользователь делает асинхронный запрос, другие пользователи должны подождать, пока это будет сделано, прежде чем делать запросы тоже?
Нет. Только тот один экземпляр этого обработчика запроса, который содержит await
, приостановлен. Другое выполнение в интерпретаторе и другом обработчике событий через цикл событий (например, другие входящие запросы) все еще может произойти, поэтому другие запросы все еще могут быть обработаны, даже если один обработчик запросов находится в an await
. Это иллюстрирует, как await
не блокируется и не приостанавливается весь интерпретатор, а только выполнение одной функции.
Когда я отправил запрос на первый маршрут и в ожидании ответа отправил еще один запрос на второй маршрут, и я получил ответ, хотя первый маршрут еще не завершил выполнение кода. почему в этом примере он не блокируется?
Только первый маршрут был приостановлен await
. Другие события и другие входящие запросы все еще могут быть обработаны просто отлично.
Комментарии:
1. хорошо объяснено, спасибо.
Ответ №2:
NodeJS не является однопоточным. Вы можете проверить это с помощью следующих шагов:
- Создайте этот файл js, а затем запустите его:
while(true)
- На терминале вы выполняете эту команду, чтобы получить количество потоков, используемых для запуска этого файла js.
NUM=
ps M <pid> | wc -l
amp;amp; эхо-номер потока: $((NUM-1))
И вы можете видеть, что while(true)
используется более 1 потока.
Давайте вернемся к вашему коду, причине, по которой вы можете получить результат запроса /route2
немедленно, когда ваш запрос /route1
еще не завершен, потому что NodeJS, использующие EventLoop для выполнения асинхронных функций, не блокируют основной поток. Когда вы вызываете функцию сна, NodeJS запустит таймер, а затем выведет ваш обратный вызов из стека вызовов(поэтому запрос на /route2
не блокируется /route1
), и когда ваш таймер resolve(true)
закончится, он будет помещен в очередь событий, и с помощью EventLoop ваш обратный route1
вызов будет выполнен.
Комментарии:
1. Nodejs запускает только один поток вашего Javascript. Именно это подразумевается под однопоточностью в nodejs. Да, он использует несколько других потоков для собственного внутреннего ведения, но эти другие потоки не являются потоками вашего Javascript, если вы не задействовали рабочие потоки, которые затем запускают целые дополнительные виртуальные машины, а не только поток.