NodeJS 中的阻塞与非阻塞
Blocking vs Non-Blocking in NodeJS
我们都知道 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")
})
await
仅 blocks/suspends 执行当前函数,而不是整个解释器。事实上,当一个函数命中函数内部的第一个 await
时,该函数立即 returns 承诺和该函数之后的其他处理(或发生的其他事件)可以自由 运行.
因此,在您的示例中,当它到达 await sleep()
时,该函数的执行将暂停,直到 await
resolves/rejects 和包含的 async
函数立即 returns 一个未实现的承诺。由于带有 router.get()
的 Express 没有对返回的承诺做任何事情,它只是忽略它并且 returns 控制回到事件循环。稍后,您的第二个请求到达服务器,一个事件被放入 nodejs 事件队列,Express 被调用并为您的第二个路由处理程序提供服务。
so if a user make an async request other users should wait for it to be done before making requests too?
没有。只有包含 await
的那个请求处理程序的一个实例被挂起。解释器中的其他执行和事件循环中的其他事件处理程序(例如其他传入请求)仍然可以发生,因此仍然可以处理其他请求,即使一个请求处理程序位于 await
。这说明了 await
如何不阻塞或暂停整个解释器,只执行一个函数。
When I sent a request to first route and while waiting for a response I sent another request to the second route and I got a response even though the first route didn't finish executing the code yet. why is it non-blocking on this example?
只有第一条路线被 await
暂停。其他事件和其他传入请求仍然可以正常处理。
NodeJs 不是单线程。
您可以通过以下步骤进行检查:
创建一个这个 js 文件,然后 运行 它:
while(true)
在终端上执行此命令以获取使用 运行 此 js 文件的线程数。
NUM=ps M <pid> | wc -l
&& 回显线程数为:$((NUM-1))
并且您可以看到 while(true)
使用了超过 1 个线程。
让我们回到您的代码,
之所以在/route1
的请求还没有完成的时候,可以立即得到/route2
的请求结果,是因为NodeJs使用EventLoop使得异步函数不会阻塞主线程。
当您调用睡眠函数时,NodeJs 将启动一个计时器,然后将您的回调从调用堆栈中取出(这就是为什么对 /route2
的请求不会被 /route1
阻塞),并且当您的计时器结束时resolve(true)
将被放入 EventQueue,在 EventLoop 的帮助下,您对 route1
的回调将被执行。
我们都知道 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")
})
await
仅 blocks/suspends 执行当前函数,而不是整个解释器。事实上,当一个函数命中函数内部的第一个 await
时,该函数立即 returns 承诺和该函数之后的其他处理(或发生的其他事件)可以自由 运行.
因此,在您的示例中,当它到达 await sleep()
时,该函数的执行将暂停,直到 await
resolves/rejects 和包含的 async
函数立即 returns 一个未实现的承诺。由于带有 router.get()
的 Express 没有对返回的承诺做任何事情,它只是忽略它并且 returns 控制回到事件循环。稍后,您的第二个请求到达服务器,一个事件被放入 nodejs 事件队列,Express 被调用并为您的第二个路由处理程序提供服务。
so if a user make an async request other users should wait for it to be done before making requests too?
没有。只有包含 await
的那个请求处理程序的一个实例被挂起。解释器中的其他执行和事件循环中的其他事件处理程序(例如其他传入请求)仍然可以发生,因此仍然可以处理其他请求,即使一个请求处理程序位于 await
。这说明了 await
如何不阻塞或暂停整个解释器,只执行一个函数。
When I sent a request to first route and while waiting for a response I sent another request to the second route and I got a response even though the first route didn't finish executing the code yet. why is it non-blocking on this example?
只有第一条路线被 await
暂停。其他事件和其他传入请求仍然可以正常处理。
NodeJs 不是单线程。 您可以通过以下步骤进行检查:
创建一个这个 js 文件,然后 运行 它:
while(true)
在终端上执行此命令以获取使用 运行 此 js 文件的线程数。
NUM=
ps M <pid> | wc -l
&& 回显线程数为:$((NUM-1))
并且您可以看到 while(true)
使用了超过 1 个线程。
让我们回到您的代码,
之所以在/route1
的请求还没有完成的时候,可以立即得到/route2
的请求结果,是因为NodeJs使用EventLoop使得异步函数不会阻塞主线程。
当您调用睡眠函数时,NodeJs 将启动一个计时器,然后将您的回调从调用堆栈中取出(这就是为什么对 /route2
的请求不会被 /route1
阻塞),并且当您的计时器结束时resolve(true)
将被放入 EventQueue,在 EventLoop 的帮助下,您对 route1
的回调将被执行。