为什么在此示例中调用 setTimeout 会解除事件循环的阻塞?
以下代码来自此媒体 文章
const fastify = require("fastify")({ logger: true });
const crypto = require("crypto");
const randomstring = require("randomstring");
// Declare healthcheck ping-pong
fastify.get("/ping", (_request, reply) => {
reply.send({ data: "pong" });
});
fastify.get("/event-loop-blocking-operation", async (_request, reply) => {
let hash = crypto.createHash("sha256");
const numberOfHasUpdates = 10e6;
for (let iter = 0; iter < numberOfHasUpdates; iter++) {
hash.update(randomstring.generate());
}
reply.send({ data: "Finished doing long task" });
});
fastify.get("/event-loop-nonblocking-operation", async (_request, reply) => {
const hash = crypto.createHash("sha256");
const numberOfHasUpdates = 10e6;
const breathSpace = async (delayInS) =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(), delayInS * 1000);
});
for (let iter = 0; iter < numberOfHasUpdates; iter++) {
const hashed = hash.update(randomstring.generate());
await breathSpace(0);
}
reply.send({ data: "Finished long process" });
});
// Run the server!
const port = 3000;
fastify.listen(port, (err, address) => {
if (err) {
fastify.log.error(err);
process.exit(1);
}
fastify.log.info(`server listening on ${address}`);
});
情况 1
#in Terminal 1
$ node blocking_event_loop.js
#in Terminal 2
#calls 127.0.0.1:3000/ping every one second
#keeps printing {data: "pong"} in terminal
$ while true; do date; curl 127.0.0.1:3000/ping; sleep 1; echo \n; done;
#in Terminal 3
#blocks the event loop
$ date && curl 127.0.0.1:3000/event-loop-blocking-operation && date
调用最后一个命令后,事件循环被阻止,终端 2 停止打印。
情况 2
#in Terminal 1
$ node blocking_event_loop.js
#in Terminal 2
#calls 127.0.0.1:3000/ping every one second
#keeps printing {data: "pong"} in terminal
$ while true; do date; curl 127.0.0.1:3000/ping; sleep 1; echo \n; done;
#in Terminal 3
#does not block the event loop
$ date && curl 127.0.0.1:3000/event-loop-nonblocking-operation && date
在第二种情况下,调用最后一个命令后,终端 2 继续打印 {data: "pong"}
以及 /event-loop 中定义的操作-blocking-operation
的路由处理程序继续执行其正在执行的操作。
在循环中调用 await BreathSpace(0)
为何会以非阻塞方式运行该操作?
据我所知,当通过curl调用/event-loo-nonblocking-operation端点时:
/event-loop-nonblocking-operation
的相应路由处理程序被排队到I/ O 回调队列
也称为轮询队列- 路由处理程序从轮询队列中出队并推送到调用堆栈
- 路由处理程序的代码逐行运行 3.1 调用
hash.update
方法(在for循环的每次迭代中) 3.2 调用await BreathSpace(0)
(在 for 循环的每次迭代中) 3.2.1 (解决、拒绝)=> setTimeout(() =>solve(), 0) 被排队到计时器队列
现在考虑事件循环没有被阻塞,这是否意味着,即使循环的第一次迭代没有完成,事件循环也会改变它的相位轮询计时器来处理解决承诺的 setTimeout?它如何在不阻塞的情况下执行for循环?
The following code is from this medium article
const fastify = require("fastify")({ logger: true });
const crypto = require("crypto");
const randomstring = require("randomstring");
// Declare healthcheck ping-pong
fastify.get("/ping", (_request, reply) => {
reply.send({ data: "pong" });
});
fastify.get("/event-loop-blocking-operation", async (_request, reply) => {
let hash = crypto.createHash("sha256");
const numberOfHasUpdates = 10e6;
for (let iter = 0; iter < numberOfHasUpdates; iter++) {
hash.update(randomstring.generate());
}
reply.send({ data: "Finished doing long task" });
});
fastify.get("/event-loop-nonblocking-operation", async (_request, reply) => {
const hash = crypto.createHash("sha256");
const numberOfHasUpdates = 10e6;
const breathSpace = async (delayInS) =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(), delayInS * 1000);
});
for (let iter = 0; iter < numberOfHasUpdates; iter++) {
const hashed = hash.update(randomstring.generate());
await breathSpace(0);
}
reply.send({ data: "Finished long process" });
});
// Run the server!
const port = 3000;
fastify.listen(port, (err, address) => {
if (err) {
fastify.log.error(err);
process.exit(1);
}
fastify.log.info(`server listening on ${address}`);
});
case 1
#in Terminal 1
$ node blocking_event_loop.js
#in Terminal 2
#calls 127.0.0.1:3000/ping every one second
#keeps printing {data: "pong"} in terminal
$ while true; do date; curl 127.0.0.1:3000/ping; sleep 1; echo \n; done;
#in Terminal 3
#blocks the event loop
$ date && curl 127.0.0.1:3000/event-loop-blocking-operation && date
After calling the last command, the event loop gets blocked and Terminal 2 stops printing.
case 2
#in Terminal 1
$ node blocking_event_loop.js
#in Terminal 2
#calls 127.0.0.1:3000/ping every one second
#keeps printing {data: "pong"} in terminal
$ while true; do date; curl 127.0.0.1:3000/ping; sleep 1; echo \n; done;
#in Terminal 3
#does not block the event loop
$ date && curl 127.0.0.1:3000/event-loop-nonblocking-operation && date
In 2nd case, After calling the last command, Terminal 2 keeps printing {data: "pong"}
and the operation defined in the /event-loop-blocking-operation
's route handler keeps doing what it's doing.
How is it that calling await breathSpace(0)
in the loop will run the operation in a non-blocking way?
As far as I know when the /event-loo-nonblocking-operation endpoint is called via curl:
/event-loop-nonblocking-operation
's corresponding route handler is enqueued to theI/O callback queue
which is also known as poll queue- route handler is dequeued from poll queue and pushed to call stack
- route handler's code is run line by line
3.1hash.update
method is called (on each iteration of for loop)
3.2await breathSpace(0)
is called (on each iteration of for loop)
3.2.1 the (resolve, reject) => setTimeout(() => resolve(), 0) is enqueued to the Timer queue
Now considering event loop not getting blocked, Does this mean, that even though the first iteration of the loop is not done, event loop changes it's phase from poll to timer to handle the setTimeout which resolves the promise? How does it do the for loop without blocking?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论