How to order the order of returned API calls with generators?

46
June 16, 2019, at 1:20 PM

I'm practicing some more advanced Javascript techniques, and came across generators and iterators as something I wanted to look into. I know that I'm doing this incorrectly, but I'm not really sure how to go about it.

The idea of my little program is this: I want to make API calls to the OpenWeather API for four (or more, but I'm testing with four) cities. The cities are stored in an array and one by one, the city is appended to the URL and a fetch request is sent. Each response is appended to an array and the array is sent to the client. This was my original code:

// node/express setup here
const cities = ["London%2Cuk", "New York%2Cus", "Johannesburg%2Cza", 'Kingston%2Cjm']
const url = process.env.URL_BASE;
const headers = {
    "X-RapidAPI-Host": process.env.HOST,
    "X-RapidAPI-Key": process.env.API_KEY
}
const requestInit = { method: 'GET',
           headers: headers
        };
const fetchWeather = (ep) => {
    const appendedURL = url + ep;
    return fetch(appendedURL, requestInit)
        .then(r => r.json());
}
app.get('/', (req, res, err) => {
     const data = []
     Promise.all(
        cities.map( async (city) => {
            await fetchWeather(city)
            .then(returns => {
                data.push(returns)
            })
         })
     )
     .then(() => {
        res.send(data)
        return data;
    })
    .catch(err => console.log(err))   
})

Right? Solid, works ok. But now I'm stuck on how to order it. The way I would think to do this is to switch await fetchWeather(city) to yield fetchWeather(city) and have a generator manager that would continue calling next(city) until the array had completed, but I'm having an issue figuring out the pattern. I refactored the api call to a generator and am testing out a generator management function.

The paradigm I have based on my understanding is this:

  • First .next() starts the iteration
  • Second .next(args) passes the designated city to the first yield
  • Third .next() sends the yielded fetch request and should (ideally) return the response object that can be .then()'d.

Here is my tester generator code:

function *fetchWeather() {
    for (let i = 0; i < cities.length; i++){
        const appendedURL = url + (yield);
         yield fetch(appendedURL, requestInit)
        .then(r => {
            return r.json()
        });
    }
}
const generatorManager = (generator) =>{
    if (!generator) {
        generator = fetchWeather();
    }
    generator.next()
    generator.next(cities[i])
    generator.next().value.then( e => 
        console.log(e));
}

I'm getting an error:TypeError: Cannot read property 'then' of undefined And I'm not sure where I'm going wrong here with my logic. How do I refactor this to allow me to wait for specific promises if I can't individually pass known values? I know there has to be a way, but I'm missing something.

Thanks in advance.

Answer 1

I don't understand what benefit you hope to get from using a generator here, but the reason you're getting that error is you're doing one to many .next()'s

The first generator.next() runs fetchWeather until the first yield, which is the yield at the end of const appendedURL = url + (yield);. The return value from calling generator.next() in this case is { value: undefined, done: false }

After that, generator.next(cities[i]) resumes fetchWeather, with cities[i] being the result of the previous yield. The generator continues running, calling fetch, then calling .then on that promise, and then yielding the resulting promise. So the return value that generatorManager sees from doing generator.next(cities[i]) is { value: /* a promise object */, done: false }.

So to fix that error, you need to reduce the number of calls you're making to generator.next

generator.next()
generator.next(cities[i]).value.then(e => 
  console.log(e));

As mentioned in the comments, the usual way i'd do this is map the cities to promises, and then do promise.all. For example:

Promise.all(
  cities.map((city) => fetchWeather(city)) // note, this is the original fetch weather, not the generator
).then((data) => {
  res.send(data);
  return data;
})
.catch(err => console.log(err))
READ ALSO
Why are elements in 3d rendering at the wrong size only at certain angles of perspective and rotation?

Why are elements in 3d rendering at the wrong size only at certain angles of perspective and rotation?

I'm trying to create a 3d hallway using css and react inline stylesThe function mimics a first-person perspective by translating and rotating the cuboid in relation to mouse position and arrow key inputs

38
How can I create an image overlay blend mode algorithm like on Photoshop?

How can I create an image overlay blend mode algorithm like on Photoshop?

I'm working on a code that should blend 2 images with each otherThe effect should be similar to Photoshop overlay blend mode or screen mode

59
How to print current location using GPS?

How to print current location using GPS?

I want to print current city, state and countryI used IP tracking but that is not much accurate and displaying me different cities, i want current city

39
react-native : How to format object value properly

react-native : How to format object value properly

I am trying to create an empty objectFirst, each field will have a name with empty values

17