admin管理员组

文章数量:1178539

I'd like to have a function like this:

export async function* iterateDir(dir: string) {
    let list = await fs.readdir(dir); // fs-promise implementation of readdir
    for (let file of list) {
        yield file;
    }
}

Which I would use like:

for (let file in iterateDir(dir)) {
    processFile(file);
}

This doesn't work because a function cannot be both async and a generator.

How would I structure the code to achieve the same?

  1. If I change the await fs.readdir to callbacks, I assume the outer for..of loop would not wait.
  2. If I get rid of the generator and the directory is huge, iterateDir() will be slow.

For reference: async generator function proposal

I'd like to have a function like this:

export async function* iterateDir(dir: string) {
    let list = await fs.readdir(dir); // fs-promise implementation of readdir
    for (let file of list) {
        yield file;
    }
}

Which I would use like:

for (let file in iterateDir(dir)) {
    processFile(file);
}

This doesn't work because a function cannot be both async and a generator.

How would I structure the code to achieve the same?

  1. If I change the await fs.readdir to callbacks, I assume the outer for..of loop would not wait.
  2. If I get rid of the generator and the directory is huge, iterateDir() will be slow.

For reference: async generator function proposal

Share Improve this question edited Nov 3, 2016 at 16:43 Borek Bernard asked Nov 3, 2016 at 16:05 Borek BernardBorek Bernard 53.2k62 gold badges176 silver badges246 bronze badges 7
  • If your async work is done before your generator work, then you can just make them different functions. – Stephen Cleary Commented Nov 3, 2016 at 16:18
  • The point is that the generator itself needs to be async (it awaits filesystem operations). – Borek Bernard Commented Nov 3, 2016 at 16:21
  • Generators cannot be asynchronous (yet). The code you posted doesn't need to be an asynchronous generator; it can be split into an asynchronous part and a generator part. – Stephen Cleary Commented Nov 3, 2016 at 16:31
  • 1 Well, in my specific case, iterateDir would be recursive. For every file, there would be a detection whether it's a directory or not, and if it is, call iterateDir recursively. In which case I don't know how to split the async and generation functionality. Which is the point of the question.. – Borek Bernard Commented Nov 3, 2016 at 16:41
  • 1 In that case, I recommend using observables (Rx.JS). – Stephen Cleary Commented Nov 3, 2016 at 20:57
 |  Show 2 more comments

2 Answers 2

Reset to default 38

This is supported in TypeScript 2.3 - tracked issue

It introduces a few new types, notably:

interface AsyncIterable<T> {
    [Symbol.asyncIterator](): AsyncIterator<T>;
}

but most importantly it also introduces for await ... of

for await (const line of readLines(filePath)) {
  console.log(line);
}

where

async function* readLines(path) {
   //await and yield ...
}

Be aware that if you want to try this you will need to configure typescript to let it know you have run-time support (add "esnext.asynciterable" to lib list) you will probably need to polyfill Symbol.asyncIterator. see TS2318: Cannot find global type 'AsyncIterableIterator' - async generator

Stream of future values is called Observable. So the most natural thing is to use a library like RxJS or most.js. If you don't want to introduce a new complex library only to use it once, use the good old approach with callbacks:

const path = require("path");
const fs = require("mz/fs");

async function listDirectory(dirName, cb, level = 0) {
  for (let fileName of await fs.readdir(dirName)) {
    let absName = path.resolve(dirName, fileName);
    let isDirectory = (await fs.stat(absName)).isDirectory();
    cb({ level, fileName });
    if (isDirectory) {
      await listDirectory(absName, cb, level + 1);
    }
  }
}

listDirectory(".", ({ level, fileName }) => {
  console.log(" ".repeat(level) + fileName);
});

If you try to convert the callback to some better abstraction, you will rediscover RxJS sooner or later. It's similar to converting one-shot callbacks to promises.

本文标签: javascriptTypeScript async generatorStack Overflow