Watching the File System

Watching drives every development server you use: change a file, the process reloads. fsPromises.watch gives you an async iterator of change events, and an AbortSignal stops it.

Watching a directory tree with debounced eventsJavaScript
import { watch, writeFile, mkdir, rm } from 'node:fs/promises';
import { setTimeout as delay } from 'node:timers/promises';
const ac = new AbortController();
setTimeout(() => ac.abort(), 1200).unref();      // stop the watcher after 1.2 s
const pending = new Set();
let timer = null;
(async () => {                                   // make some changes to observe
  await delay(100);
  await writeFile('src/app.js', 'const a = 1;\n');
  await mkdir('src/lib', { recursive: true });
  await writeFile('src/lib/util.js', 'export const x = 1;\n');
})();
try {
  const options = { recursive: true, signal: ac.signal };
  for await (const { eventType, filename } of watch('src', options)) {
    pending.add(`${filename} (${eventType})`);
    clearTimeout(timer);                         // debounce the burst
    timer = setTimeout(() => console.log('changed:', [...pending].sort().join(', ')), 120);
  }
} catch (err) {
  if (err.name !== 'AbortError') throw err;
  console.log('watcher: stopped cleanly');
}
await rm('src/lib', { recursive: true, force: true });
Output
changed: app.js (change), lib (change), lib (rename), lib\util.js (change), lib\util.js
  (rename)
watcher: stopped cleanly

Three things deserve attention. One logical edit produced several events — creating lib/util.js fired both rename and change, and touched the parent directory too — which is why the debounce timer exists. eventType is only ever 'rename' or 'change', never "created" or "deleted"; stat the path to tell them apart. And aborting rejects the iterator with an AbortError, so the catch block is mandatory.

fs.watchFile() is the fallback: it polls stat on an interval, so it works anywhere at the cost of latency and CPU. For production-grade watching, chokidar 5.0.0 12,245 (github.com/paulmillr/chokidar (https://github.com/paulmillr/chokidar 12,245 ), npm 2,036 i chokidar) normalizes the platform differences, waits for a file to finish being written before reporting it, and emits real add/change/unlink events. Before installing it, check whether node --watch (Watch Mode and Scripts) already solves your problem.