-2

So I realize I have a scoping problem going on here. I'm hitting an API to return a list of films. The response includes an array of film objects. Each film object has an array of characters, but each character is just an endpoint. The goal is to list each film with the first 3 characters associated with that particular film. What is going to be the most efficient way to accomplish this? Here is the code I currently have, which I know is wrong, but should convey what I'm trying to do fairly clearly.

import {getCharByUrl, getFilms} from './api/swapiApi';

getFilms().then(result => {
    let films = result.results; // Array of films
    let episodes = ``;

    films.forEach(film => {
        let urls = film.characters; // Array of endpoints to hit
        let chars = `<ul>`;

        for (let i = 0; i < 3; i++) {
            getCharByUrl(urls[i]).then(character => {
                chars += `<li>${character.name}</li>`;
            });
        }

        chars += `</ul>`;

        episodes += `
            <div class="col-sm-4">
                <div>${film.title}</div>
                <div>${film.director}</div>
                ${chars}
                <hr>
            </div>
        `;
    });

    global.document.getElementById('films').innerHTML = episodes;
});

Code that worked for me:

import {getCharByUrl, getFilms} from './api/swapiApi';

getFilms().then((result) => {
    let films = result.results; // Array of films
    let episodes = ``;
    var Promise = require('es6-promise').Promise;

    films.forEach((film) => {
        let urls = []; // Array of endpoints to hit

        for(let i = 0; i < 3; i++) {
            urls.push(film.characters[i]);
        }

        let chars = `<ul class="list-unstyled">`;

        Promise.all(urls.map((url) => {
            return getCharByUrl(url);
        })).then((data) => {
            // data will be an array of the responses from all getCharByUrl requests
            data.forEach((character) => {
                chars += `<li>${character.name}</li>`;
            });

            chars += `</ul>`;
        }).then(() => {
            episodes += `
                <div class="col-sm-4">
                    <div>${film.title}</div>
                    <div>${film.director}</div>
                    ${chars}
                    <hr>
                </div>
            `;
            global.document.getElementById('films').innerHTML = episodes;
        });
    });
});
Dubster
  • 21
  • 6
  • 2
    No idea, you haven't actually told us what's going in this question.... Does `result` exist? What's in it? What's in a `film` object, etc? Right now there is no problem that needs solving in this question. – Mike 'Pomax' Kamermans Apr 27 '17 at 22:46
  • Possible duplicate of [Node.js: Best way to perform multiple async operations, then do something else?](http://stackoverflow.com/questions/26268651/node-js-best-way-to-perform-multiple-async-operations-then-do-something-else) – Heretic Monkey Apr 27 '17 at 23:01
  • No, you don't have a scope problem, you have an [asynchrony problem](http://stackoverflow.com/q/23667086/1048572) – Bergi Apr 27 '17 at 23:06
  • Yes @Mike'Pomax'Kamermans, of course result exists, it's an array of film objects. I'm using 3 attributes: title, director, and characters. Title and director are just strings, but characters is another array of strings which are endpoints that need to also be hit to retrieve the information for the particular character. The variable chars doesn't have the added characters when being used in episodes definition. For the most part, the correct answer has already been given. – Dubster Apr 27 '17 at 23:50
  • @Dubster don't tell "me" in a comment (I might not be the one to actually give you the answer, I'm just driving by, see a bad post, and point out you need to do a bit more work to [ask a good question](/help/how-to-ask)), make sure to tell everyone by putting that information in the post. – Mike 'Pomax' Kamermans Apr 28 '17 at 22:21

1 Answers1

0

I'd take a look at Promise.all: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all. Not positive without seeing the api responses but I think you could do something like this:

    import {getCharByUrl, getFilms} from './api/swapiApi';
    getFilms().then((result) => {
        let films = result.results; // Array of films
        let episodes = ``;

        films.forEach((film) => {
            let urls = film.characters; // Array of endpoints to hit
            let chars = `<ul>`;

            Promise.all(urls.map((url) => {
                return getCharByUrl(url);
            })).then((data) => {
                // data will be an array of the responses from all getCharByUrl requests
                data.forEach((character) => {
                    chars += `<li>${character.name}</li>`;
                });

                chars += `</ul>`;
            }).then(() => {
                episodes += `
                <div class="col-sm-4">
                    <div>${film.title}</div>
                    <div>${film.director}</div>
                    ${chars}
                    <hr>
                </div>
            `;
            global.document.getElementById('films').innerHTML = episodes;
            });
        });
    });
n_burt
  • 18
  • 4
  • That's a step in the right direction, but in no way complete. Everything after the asynchronous loop(s!) must go in a promise callback – Bergi Apr 27 '17 at 23:07
  • yup, thanks for catching that, I've updated the code – n_burt Apr 27 '17 at 23:11
  • Yes. And now apply the same thing to the use of `episodes` after the outer loop. – Bergi Apr 27 '17 at 23:13
  • This is just about right. I'm going to go ahead and make this as being the right answer. 2 issues. 1 - It's hitting every endpoint instead of the first 3 for each film. 2 - The innerHTML needs to be placed in the final then. Thanks for the help. – Dubster Apr 27 '17 at 23:44
  • No, that is not what I meant. I was referring to the last statement. – Bergi Apr 27 '17 at 23:58
  • Ok updated #2. For #1 you can use slice before mapping over the urls. `urls.slice(0,3).map` – n_burt Apr 28 '17 at 12:11