Nodejs异步事件触发遇到的问题

Nodejs异步事件触发遇到的问题

有如下代码

'use strict';
const
    fs = require('fs'),
    filename = process.argv[2];
if (!filename) {
    throw Error('bababa...');
}
fs.watch(filename, function(event, filename) {
    console.log('bababa...');
});
console.log('bababa...');

// 在这个时间段内的事件触发之后, 没有任何结果?
const startTime = new Date().getTime();
while (new Date().getTime() < startTime + 5000);

在while执行的事件段内触发的fs.watch事件, 为什么没有结果?

是不是说, 在主进程执行的过程中, 所有触发的事件都没有效果?


2 回复

Node.js 异步事件触发遇到的问题

问题描述

在下面的代码中,我们使用 fs.watch 来监视一个文件的变化。但是,在一个长时间运行的循环中,fs.watch 触发的事件却没有任何输出。

'use strict';

const fs = require('fs');
const filename = process.argv[2];

if (!filename) {
    throw Error('Please provide a filename as an argument.');
}

fs.watch(filename, function(event, filename) {
    console.log(`File changed: ${event}`);
});

console.log('Watching file...');

// 这个长时间运行的循环会阻塞事件循环
const startTime = new Date().getTime();
while (new Date().getTime() < startTime + 5000);

问题核心

为什么在 while 循环期间,fs.watch 触发的事件没有结果?

这是因为 Node.js 是基于事件循环(Event Loop)的单线程模型。当主线程被长时间运行的循环(如上面的 while 循环)阻塞时,事件循环无法处理其他任务,包括 fs.watch 触发的事件。因此,这些事件不会被处理,也就不会有输出。

示例代码解释

  1. 引入模块和参数检查

    const fs = require('fs');
    const filename = process.argv[2];
    
    if (!filename) {
        throw Error('Please provide a filename as an argument.');
    }
    

    这里我们引入了 fs 模块,并从命令行参数中获取文件名。如果没有提供文件名,则抛出错误。

  2. 设置文件监听

    fs.watch(filename, function(event, filename) {
        console.log(`File changed: ${event}`);
    });
    

    使用 fs.watch 监听文件变化。当文件发生变化时,回调函数会被调用并打印相关信息。

  3. 长时间运行的循环

    const startTime = new Date().getTime();
    while (new Date().getTime() < startTime + 5000);
    

    这是一个简单的循环,它会持续运行 5 秒钟。由于这个循环会一直占用 CPU 时间,事件循环无法处理其他任务。

解决方案

要解决这个问题,可以避免阻塞事件循环的操作。例如,可以使用 setTimeoutsetInterval 来替代长时间运行的循环:

'use strict';

const fs = require('fs');
const filename = process.argv[2];

if (!filename) {
    throw Error('Please provide a filename as an argument.');
}

fs.watch(filename, function(event, filename) {
    console.log(`File changed: ${event}`);
});

console.log('Watching file...');

// 使用 setTimeout 避免阻塞事件循环
setTimeout(() => {}, 5000);

这样,fs.watch 触发的事件就能正常被处理了。


在你的代码中,fs.watch 是一个异步操作,用于监听文件的变化。然而,由于你在主进程中使用了一个 while 循环来阻塞主线程,导致 Node.js 无法处理这些异步事件。

具体来说,fs.watch 监听到文件变化后会将事件推送到事件队列中,等待主线程处理。但由于 while 循环持续运行,主线程被阻塞,无法从事件队列中取出并处理这些事件,因此你观察不到任何输出。

为了验证这一点,可以去掉 while 循环,并在终端中手动修改文件内容,你应该能够看到 'bababa...' 被打印出来。

示例代码改进

'use strict';
const fs = require('fs');
const filename = process.argv[2];

if (!filename) {
    throw Error('Filename is required.');
}

fs.watch(filename, function(event, filename) {
    console.log(`File ${filename} has been changed. Event: ${event}`);
});

console.log('Watching for changes in the file...');

解释

  1. 异步事件处理fs.watch 是异步操作,会在文件发生变化时触发回调函数。
  2. 主线程阻塞:如果你在主进程中使用 while 循环,会导致主线程被阻塞,所有异步事件都无法得到及时处理。
  3. 避免阻塞:不要使用长时间运行的同步循环,而是让 Node.js 的事件循环自然处理异步事件。

通过上述改进,你可以更清楚地看到 fs.watch 的效果。

回到顶部