Jaquarh
Jaquarh

Reputation: 6693

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

Upvotes: 0

Views: 82

Answers (2)

CertainPerformance
CertainPerformance

Reputation: 371233

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.

Upvotes: 3

Code-Apprentice
Code-Apprentice

Reputation: 83597

One solution is to do

const resp = await Care.api();
const car = (new self({
                x: 0,
                y: 250,
                colour: "#" + resp.colors[3].hex,
                windowsColour: "#" + resp.colors[2].hex,
                number: i
            })).draw();
yield car;

This should at least get you closer to something that actually works.

The disadvantage is that it blocks for each request rather than letting each one resolve asynchronously. I still think it's a good place to start to at least see if your code works the way you want. Then follow up with some modifications to correctly take advantage of async API calls. (I don't know the exact solution for that next step off the top of my head, though.)

Upvotes: 1

Related Questions