Search code examples
javascriptiteratorgeneratoryield

Generator in JavaScript not working due to being wrapped in an async request


I want to return a generator for the cars being created. The issue is, I nest an API to receive some random colours for the cars upon which I cannot yield to since they're not declared as generator functions.

I tried to create the reference using let car but because the request is async, it yields before the car is instanced. Perhaps I need to pass this as reference? Any ideas?

static async api()
{
    return (await fetch('http://www.colr.org/json/colors/random/8')).json();
}

static* createCars(n)
{
    for(let i = 1; i <= n; i++) {
        Car.api().then(resp => {
            let car = (new self({
                x: 0,
                y: 250,
                colour: "#" + resp.colors[3].hex,
                windowsColour: "#" + resp.colors[2].hex,
                number: i
            })).draw();
        });
        
        yield car;
    }
}

Uncaught ReferenceError: car is not defined


Solution

  • Use an async generator instead, so that you can await in the main body of the generator function. This allows you to retrieve the asynchronous value at the top level of the function, so you can yield it. (You can't yield inside callbacks, after all.)

    class Car {
      static async api() {
        return (await fetch('http://www.colr.org/json/colors/random/8')).json();
      }
    
      static async * createCars(n) {
        for (let i = 1; i <= n; i++) {
          const resp = await Car.api();
          /*
              let car = (new self({
                  x: 0,
                  y: 250,
                  colour: "#" + resp.colors[3].hex,
                  windowsColour: "#" + resp.colors[2].hex,
                  number: i
              })).draw();
              yield car;
          */
          yield "#" + resp.colors[3].hex;
    
        }
      }
    }
    
    (async () => {
      for await (const color of Car.createCars(1)) {
        console.log(color);
      }
    })();
    

    Note that colr.org has standard CORS protections, so you can't fetch to it from a different domain on the front-end - you'll either need to request a different working endpoint, or bounce the request off your own server, or something like that.