2021-04-14
1135
#vanilla javascript
Paul Cowan
42536
Apr 14, 2021 ⋅ 4 min read

JavaScript generators: The superior async/await

Paul Cowan Contract software developer.

Recent posts:

How to use TOON to reduce your token usage by 60%

TOON is a lightweight format designed to reduce token usage in LLM prompts. This post breaks down how it compares to JSON, where the savings come from, and when it actually helps.

Rosario De Chiara
Dec 10, 2025 ⋅ 5 min read
Fixing AI Generated Code

Fixing AI-generated code: 5 ways to debug, test, and ship safely

Andrew Evans, principal engineer and tech lead at CarMax discusses five ways to fix AI-generated code and help you debug, test, and ship safely.

Andrew Evans
Dec 10, 2025 ⋅ 9 min read
Apple Liquid Glass LogRocket

How to create Liquid Glass effects with CSS and SVG

This tutorial walks through recreating Apple’s Liquid Glass UI on the web using SVG filters, CSS, and React. You’ll learn how to build refraction and reflection effects with custom displacement and specular maps, and how to balance performance and accessibility when using advanced filter pipelines.

Rahul Chhodde
Dec 8, 2025 ⋅ 10 min read
tRPC vs ORPC: Which is better for your next TypeScript project, and why?

tRPC vs oRPC: Which is better for your next TypeScript project, and why?

tRPC solved type safety for full-stack TypeScript teams. oRPC borrowed the best parts and added interoperability. This article breaks down how both frameworks work and where each one fits best.

Temitope Oyedele
Dec 8, 2025 ⋅ 22 min read
View all posts

6 Replies to "JavaScript generators: The superior async/await"

  1. I think you are misunderstanding the differences between promises and yield. Just about all of your examples are simpler with async versions and the error handling is far easier than with yield. Especially when nested. You also seem to be confusing asynchronously I/O with yielding during iteration… They are vastly different use cases and the last thing you want to do for asynchronous I/O is yield back to the caller.

  2. I’m not sure if author knows what he is writing about. About sample about stopping websocket is not good idea cause what will happen when we stop execution of generator – > websocket got an issue – > we resumes generator – > error occurs, i think writing this code in async await manner will be much more simple in case of error handling

  3. 1. I think you are misunderstanding the differences between promises and yield. Just about all of your examples are simpler with async versions and the error handling is far easier than with yield.
    2. Especially when nested.
    3. You also seem to be confusing asynchronously I/O with yielding during iteration…
    4. They are vastly different use cases and the last thing you want to do for asynchronous I/O is yield back to the caller.

    This is 1 opinion followed by 3 assertions. Do you have evidence for any of these claims?

  4. That last example doesn’t really show any benefit over the async await experience. Also I’m wary of anything in javascript that uses “process” semantics. Under the hood JS engines handle a single function call at a time so suggesting otherwise seems disingenuous. Unless the idea is that multiprocess patterns are just inherently more readable? That’s even more dubious.

    Here’s an async/await equivalent to your flakeyconnection example. It’s written fairly quickly but it should work. Whether or not one is more confusing than the other is a matter of debate, but a point in favor of the async await example is that it introduces no new libraries or concepts other than the standard:

    const slep = (ms: number) => {
    return new Promise((resolve) => setTimeout(resolve, ms));
    }
    const someApiCall = async () => {
    throw new Error(“oops”);
    }
    const flakey= async () => {
    let tries = 5;
    let result = undefined;
    while (tries > 0) {
    try {
    const r = await someApiCall();
    } catch (e) {
    }
    tries–;
    await slep(1000);
    }

    if(!result) {
    // do something, probably throw error
    }

    return result;
    }

    Another benefit of the async/await example is that it’s exactly the same pattern as if you had a flakey function that was not async.

  5. Ah actually my previous comment was in error. Your example was for a timeout, not for a number of re-tries.

    In that case the promise version would be slightly altered:

    const timeout = (secs:number) => {
    return new Promise((resolve, reject) => {
    setTimeout(() => reject(“timeout exceeded”), secs);
    });
    }

    const sleep = (secs:number) => {
    return new Promise((resolve, reject) => {
    setTimeout(resolve, secs);
    });
    }
    const someApiCall = async () => {
    throw new Error(“oops”);
    }
    const flakey = async () => {
    while (true) {
    try {
    return await someApiCall();
    } catch (e) {
    }
    await sleep(1000);
    }
    }

    const main = () => {
    try {
    const result = Promise.race([flakey(), timeout(500)]);
    } catch(e) {
    // time out exceeded.
    }
    }

    Still more readable than the generator version. Especially the Promise.race(…) makes it immediately obvious that this code is dealing with “timeouts”. Whereas in the generator version, we have to read through the whole setup to understand fully what’s going on. Which, honestly is why I missed the point of your example in the first place. Granted, that is just me being the lowest common denominator 😀

Leave a Reply

Hey there, want to help make our blog better?

Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now