How can you use the Deno.watchFs infinite async iterable without blocking?

323 Views Asked by At

So Deno has a filesystem watcher API that seems... obtuse to me. Perhaps that's simply due to my ignorance of async iterators. It's shown to be used like so:

const watcher = Deno.watchFs("/directory")
for await (const event of watcher) {
    //process events
}
//code here is never executed

watcher is an async iterable which is infinite, which means this for loop will block forever. What is a sensible way to write code to process these events such that it will not block the function?

Aside: why on earth did they not simply have a callback / event listener style interface?

2

There are 2 best solutions below

5
Matt On BEST ANSWER

Don't have anything after the loop, other than code you want to run after the FsWatcher is closed.

async function watchFs(){
  const watcher = Deno.watchFs("/directory")
  for await (const event of watcher) {
      //process events
  }
  // some finalisation
}

const watcher = watchFs().catch(handleSomething)
moreThings()

It's a different way of displaying the same concept. I wouldn't argue that async/await "is a bad design" when it's doing what was intended of showing you the flow of an asynchronous program in a sequential view.

async function watchFs(){
  const stream = stream.watchFs("/directory")

  stream.on('change', (event) => {
     // process events
  })

  stream.on('end', () => {
     // some finalisation
  }

  stream.on('error', (error) => {
      handleSomething()
  })
 
  return stream 
}

const watcher = watchFs()
0
jsejcksn On

If you want to run any for await...of loop asynchronously in the same scope as your other code without awaiting its completion, you'll have to wrap it in another async function and call that function without the await keyword.

You can create that kind of abstraction without much code:

async function forEachAsync<T>(
  asyncIterable: AsyncIterable<T>,
  callback: (result: T) => void,
) {
  for await (const result of asyncIterable) callback(result);
}

const watcher = Deno.watchFs(Deno.cwd());

forEachAsync(watcher, (fsEvent) => {
  const { kind, ...rest } = fsEvent;
  console.log(kind, rest);
}).catch((reason) => {/* handle */});

console.log("Watching in progress");

// Close it whenever you want (e.g. after 2s):
setTimeout(() => {
  watcher.close();
  console.log("Finished watching");
}, 2e3);