Nodejs中关于事件循环

Nodejs中关于事件循环

在看《node.js入门经典》这本书的时候看到: 由于事件循环是以单一进程为基础的,所以为了确保高性能需要遵循以下原则: 1,函数必须快速返回。2,函数不得阻塞。3,长时间运行的操作必须移到另一个进程中去 但是之前在看《深入浅出node.js》的时候,说异步I/O在windows下是通过IOCP的线程池执行的,不管它是否阻塞I/O, 都不会影响到javascript线程的后续执行的,这和上面说的函数必须快速返回是不是矛盾啊? 求大神指点一下:为什么函数必须快速返回?

5 回复

Nodejs中关于事件循环

在Node.js中,事件循环是一个核心概念,理解它对于编写高效、响应迅速的应用程序至关重要。让我们来探讨一下你提到的问题,并澄清一些关键点。

事件循环的基础

Node.js使用事件驱动、非阻塞I/O模型,这意味着它可以在等待I/O操作(如文件读写或网络请求)时继续处理其他任务。事件循环是Node.js内部机制的一部分,负责管理这些事件和回调函数的调度。

函数必须快速返回的原因

  1. 保持事件循环高效:事件循环的每个阶段都有一个固定的时间预算。如果某个回调函数耗时过长,就会占用其他任务的时间,从而导致整体性能下降。

  2. 避免阻塞:长时间运行的任务会阻塞事件循环,使得其他任务无法得到及时处理。例如,如果你有一个计算密集型的任务,它可能会占用大量时间,导致其他I/O操作得不到及时处理。

示例代码

假设我们有一个简单的HTTP服务器,它需要处理一个长时间的计算任务:

const http = require('http');

function longRunningTask() {
    let sum = 0;
    for (let i = 0; i < 1000000000; i++) {
        sum += i;
    }
    return sum;
}

http.createServer((req, res) => {
    const result = longRunningTask(); // 这里会阻塞事件循环
    res.end(`Result: ${result}`);
}).listen(3000, () => {
    console.log('Server running on port 3000');
});

在这个例子中,longRunningTask函数会阻塞事件循环,直到计算完成。这会导致其他请求无法被及时处理,影响整体性能。

如何改进

你可以将长时间运行的任务移到一个单独的进程中,或者使用Worker Threads(在Node.js 10+版本中可用):

const { Worker } = require('worker_threads');

function longRunningTask() {
    return new Promise((resolve) => {
        const worker = new Worker(`
            function longRunningTask() {
                let sum = 0;
                for (let i = 0; i < 1000000000; i++) {
                    sum += i;
                }
                return sum;
            }
            console.log(longRunningTask());
        `);
        worker.on('message', resolve);
    });
}

http.createServer(async (req, res) => {
    const result = await longRunningTask();
    res.end(`Result: ${result}`);
}).listen(3000, () => {
    console.log('Server running on port 3000');
});

在这个改进的例子中,我们使用了worker_threads模块,将长时间运行的任务放在一个单独的进程中执行,从而避免阻塞事件循环。

总结

虽然异步I/O在Windows下通过IOCP的线程池执行,可以避免阻塞JavaScript线程,但仍然建议函数快速返回以保持事件循环的高效性。长时间运行的任务应该被移到单独的进程中或使用Worker Threads,以避免阻塞事件循环。


快速返回了就不阻塞了啊,但是没有执行完的还在继续执行,比如一个大文件还在读,但是读文件的函数已经返回了

楼主觉得函数不快速返回有什么好处…?

那些原则是为了不阻塞event loop吧。

在Node.js中,事件循环是一个核心概念,用于处理异步操作和I/O任务。为了确保高性能和响应性,Node.js的设计者提出了几个关键原则,其中之一就是函数必须快速返回。

函数必须快速返回的原因

  1. 保持事件循环流畅:Node.js使用单线程模型来处理所有JavaScript代码的执行。这意味着,任何长时间运行的任务都会阻塞整个线程,从而导致其他异步任务无法得到及时处理。

  2. 避免性能瓶颈:如果一个函数长时间运行,会占用事件循环的时间,使得其他异步任务(如定时器、I/O事件等)无法及时得到处理,从而降低整体应用的性能。

示例代码

以下是一个简单的示例,展示了如何编写一个不会阻塞事件循环的函数:

const http = require('http');

// 快速返回的示例
function quickReturnFunction(req, res) {
    // 这里不做任何耗时的操作
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello World\n');
}

// 耗时操作的例子(模拟)
function slowFunction(req, res) {
    setTimeout(() => {
        res.writeHead(200, { 'Content-Type': 'text/plain' });
        res.end('Slow Response\n');
    }, 5000); // 模拟耗时5秒
}

const server = http.createServer(slowFunction);

server.listen(3000, () => {
    console.log('Server running at http://localhost:3000/');
});

在这个例子中,quickReturnFunction 是一个快速返回的函数,而 slowFunction 模拟了一个耗时操作。为了保持事件循环的流畅,你可以考虑将耗时的操作移至后台执行,比如使用 setTimeoutsetInterval 来异步处理。

异步I/O与Windows下的IOCP

虽然《深入浅出Node.js》提到,在Windows下,长时间运行的I/O操作可以通过IOCP(完成端口)线程池来处理,不会阻塞JavaScript线程。但这并不意味着你可以忽略事件循环的原则。即使是IOCP线程池中的操作,也需要考虑资源的合理分配和管理,以避免对系统造成过大的负担。因此,编写高效的、快速返回的函数仍然是非常重要的。

回到顶部