2

Context

  1. In my journey into Typescript, I was advised that blocking calls should never be done inside async code.

  2. I'm also using generators, since they make directory traversal easy and avoid running out of stack space.

However, when I combine async code (in this case: readdir) with generators, the compiler complains that yield can only be used inside generators, leading me to think that the compiler is not able to combine closures, generators and async code, all in one go.

function *yyyymmGenerator(dir: string, props: Props) {
    const fs    = require("fs");
    const yyyy  = props.range.getUTCFullYear().toString();
    const mm    = props.range.getUTCMonth().toString();
    const start = `${yyyy}-${mm}`;

    const files = fs.readdir(dir, function(err, files) {
        for (let i = 0; i < files.length; i++) {
            const file: string = files[i];
            if (file.localeCompare(start) >= 0) {
                const d = `${dir}/${file}`;
                yield file;
            }
        }
    });
}

error TS1163: A 'yield' expression is only allowed in a generator body.

Questions

What would be the recommended best practice in a situation like this?

Would it be OK if I simply consider everything sync, blocking code but "wrap" the call inside a Promise?

1

1 Answer 1

3

You could do things like that (call next function inside the callback) :

const gen = yyyymmGenerator(args);

function callback(err, files) {
   if(err) return gen.throw(err);
   gen.next(files);
   // Print all files returned by the generator
   for(file of gen) {
     console.log(file);
   }
}

function *yyyymmGenerator(dir: string, props: Props) {
   const fs    = require("fs");
   const yyyy  = props.range.getUTCFullYear().toString();
   const mm    = props.range.getUTCMonth().toString();
   const start = `${yyyy}-${mm}`;

   const files = yield fs.readdir(dir, callback);

   for (let i = 0; i < files.length; i++) {
      const file: string = files[i];
      if (file.localeCompare(start) >= 0) {
         const d = `${dir}/${file}`;
         yield file;
      }
   }
}

gen.next(); // Start generator
Sign up to request clarification or add additional context in comments.

2 Comments

I understand the solution you proposed and I think it would work, in principle. However, I would like to understand if I should (or could?) consider the error message an evidence that the compiler is limited in regards to the scopes involved. Or even if it could be considered a bug in the compiler. I would like to file an issue but first I would like to understand if the compiler complains by fault or by design. Any ideas?
The compiler complains because you used yield outside a function* (in a callback), and this is logical because when your async function is called, the generator's execution doesn't stop, in your example the generator would probably be terminated before the execution of the readdir's callback

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.