Assurance of retrieving information

I am looking to extract specific information from the following website . I have implemented Promises in order to retrieve data about planets, then films associated with a particular planet object. My next goal is to access data within the species array nested inside the films array. So far, everything is functioning correctly up to this stage.

Below is my code for fetching the necessary information:

const task = planetId => {
  const url = `https://swapi.co/api/planets/${planetId}/`;
  const getPlanet = () => { // retrieving the planet by its Id
    return new Promise(function(resolve, reject) {
      https
        .get(`${url}`, function(res) {
          res.on("data", function(d) {
            const planetData = JSON.parse(d.toString());
            resolve(planetData);
          });
        })
        .on("error", function(e) {
          reject(e);
          console.error(e);
        });
    });
  };
  getPlanet().then(gotPlanet => {
    const planet = gotPlanet;
    const filmsArray = planet.films;
    const filmsArrayUrl = filmsArray.map(it => {
      return new Promise(function(resolve, reject) { // retrieving films array
        https
          .get(it, function(res) {
            res.on("data", function(d) {
              const films = JSON.parse(d.toString());
              resolve(films);
            });
          })
          .on("error", function(e) {
            reject(e);
            console.error(e);
          });
      });
    });
    Promise.all(filmsArrayUrl).then(gotFilms => {
      const filmsNew = gotFilms;
      planet.films = filmsNew;
      const speciesArray = planet.films.map(it => it.species);
      const speciesArrayUrl = speciesArray.map(it => it.map(el => { // attempting to retrieve species data
        return new Promise(function(resolve, reject) {
          https.get(el, function(res) {
            res.on('data', function(d) {
              const speciesFetched = JSON.parse(d.toString())
              resolve(speciesFetched)
            })
          }).on('error', function(e) {
            reject(e)
            console.error(e)
          })
        })
      }))
      Promise.all(speciesArrayUrl).then(species => {console.log(species)})
    });
  });
};

The final line displayed in the console shows as [Array[5], Array[20], Array[9]], where each element within the array appears as Promise {<pending>}. What modifications should be made to the code to properly fetch all species objects and present the end result - a planet containing the retrieved information on films and species within those films?

Answer №1

It would be beneficial to refactor your code for better readability. Consider abstracting out reusable components into separate promises like the getDataObject function below. This way, you can easily reuse this promise whenever an HTTP request is needed.

const getDataObject = url => fetch(url).then(res => res.json());

const task = planetId => {
    const planetUrl = `https://swapi.co/api/planets/${planetId}/`;
    let planet

    return getDataObject(planetUrl)
        .then(planetResponse => {
            // Retrieve and store planet response
            planet = planetResponse

            // Obtain all film data for the planet
            let filmsArrayUrls = planet.films.map(filmUrl => getDataObject(filmUrl));

            return Promise.all(filmsArrayUrls)
        })
        .then(allFilms => {
            // Update the planet object with film information
            planet.films = allFilms;

            // Extract species data from the films
            let speciesArray = planet.films.map(film => film.species);

            // Fetch and resolve each species element using Promise.All
            let speciesArrayUrl = speciesArray.map(species => Promise.all(species.map(el => getDataObject(el))))

            // Resolve the array of Promise.All objects inside of speciesArrayUrl
            return Promise.all(speciesArrayUrl)
      })
      .then(species => {
        // Assign species information to respective films in the planet object
        for (let i = 0; i < species.length; i ++) { 
            planet.films[i].species = species[i] 
        }
        console.log(planet)
        return planet
      })
  };
  
task(2)

Answer №2

It appears that the issue lies in how speciesArrayUrl is structured as an array within another array of promises. To resolve this, you must first flatten out the array.

An effective approach to flattening the array is by utilizing a reducer function on speciesArray, where you append

.reduce((items, item) => items.concat(item), [])
.

I have modified your example by incorporating the use of fetch for browser compatibility:

const task = planetId => {
  const url = `https://swapi.co/api/planets/${planetId}/`;
  const getPlanet = () => { // retrieving the planet based on its Id
    return fetch(url).then(res => res.json());
  };
  getPlanet().then(gotPlanet => {
    const planet = gotPlanet;
    const filmsArray = planet.films;
    const filmsArrayUrl = filmsArray.map(it => {
      return fetch(it).then(res => res.json());
    });
    Promise.all(filmsArrayUrl).then(gotFilms => {
      const filmsNew = gotFilms;
      planet.films = filmsNew;
      const speciesArray = planet.films.map(it => it.species)
        .reduce((items, item) => items.concat(item), []);
      const speciesArrayUrl = speciesArray.map(it => { // attempting to retrieve species data
        return fetch(it).then(res => res.json());
      })
      Promise.all(speciesArrayUrl).then(species => {console.log(species)})
    });
  });
};

task(2);

I have also refactored the code for improved readability:

function getPlanet(planetId) {
  return fetch(`https://swapi.co/api/planets/${planetId}/`)
    .then(res => res.json());
}

function getFilms(planet) {
  return Promise.all(planet.films.map(f => fetch(f).then(res => res.json())));
}

function getSpecies(film) {
  return Promise.all(film.species.map(s => fetch(s).then(res => res.json())));
}

getPlanet(2)
  .then(planet => getFilms(planet))
  .then(films => Promise.all(films.map(film => getSpecies(film))))
  .then(filmSpecies => [].concat(...filmSpecies)) // flattening array of films and film species
  .then(species => {
    console.log(species);
  });

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Strategies for temporarily storing values within md-list-item in AngularJS

I am attempting to populate a list with items using material icons. The issue is that the values are being added permanently when the material icon is clicked, disregarding the save and discard buttons at the bottom of the card. My goal is to add values te ...

issue encountered when filling out a dropdown menu using a JSON data structure

Seeking assistance with populating a button dropdown in angularjs. Encountering the error message: "Unexpected end of expression: data.WotcSummary "|. Any ideas on what might be causing this issue? Here is the JavaScript file code snippet: WotcDashBoard ...

Variables for NPM Configuration

After researching the best way to securely store sensitive information, I decided to utilize the config package and environment variables for added security. Here is how I implemented this setup: Created a config directory containing two files: default.js ...

What is the most effective method for verifying a selected item in Jquery UI selectable?

I'm having an issue with my image display div where users can delete selected images. The code functions correctly, but there seems to be unnecessary repetition in certain parts of it. I attempted using `$(".ui-selected").each()` to stop the ...

Correctly referencing a variable in a delayed AJAX request is crucial for ensuring the proper execution

I am facing an issue with a function called fetchAlbum. This function sets up a placeholder, sends an AJAX request, and updates the placeholder upon success. Here is the code snippet: function fetchAlbum() { albumCounter++; var albumElement = $(&a ...

Verifying Content in JavaScript

Here is a snippet of code I used to validate a Registration form on a content page. However, the validation part seems to not be functioning properly. Any assistance would be greatly appreciated. Master Page <%@ Master Language="C#" AutoEventWireup="t ...

Click the "Login" button using Jquery to gain access

Whenever I hit the Login button, a 500 Internal server error pops up in the console. Can someone guide me on the correct way to perform a POST request using jQuery? I would really appreciate any help. <button class="login100-form-btn" type=& ...

Tips for automating file uploads in HTML

I am having trouble filling the <input type="file"> element programmatically when trying to upload a file using a form. Can someone please provide me with guidance on how to accomplish this task? The method does not matter, I just want to achieve m ...

Mastering the management of various events within React Material UI components

I am working with a Material UI Switch Component and need to detect click events on it. In certain situations, I want to prevent the change event from triggering. What is the most effective way to achieve this? While I have previously used event.preventD ...

Solidjs: Implementing a Map in createStore does not trigger updates upon changes

As a beginner in solidjs, I might have missed something important. In the code snippet below, I am trying to understand an issue: const [state, setState] = createStore({ items: new Map() }); // e.g. Map<number, string> In a component, suppose I want ...

Select a user at random from the reactions in the message

Is there a way to select a user at random from message reactions on Discord? Despite going through all the documentation, I'm still unsure about how to do this. ...

What is the reason behind the NextJS logo not being displayed when accessing the page via its URL?

My logo isn't displaying on the /page URL View Screenshot Check out my components/LayoutWrapper.js import Image from 'next/image' import icon from '../assets/images/Icon.svg' <div className="flex items-ce ...

Incorrect measurement of text size

My attempt at creating a basic font size changer was working perfectly until I integrated it with the bootstrap framework. Strangely, when I try to increase the font size, it actually decreases instead. var baseFontSize; (function () { "use strict"; ...

Vows: proceed to the subsequent error handling process

Can you explain how to properly call the next error function using promise chaining? I initially believed that placing a return statement within the error function would automatically trigger the next error function. //This code is executed in a contr ...

Switching from using jQuery to Mootools in a short script that helps to balance the heights of

I have a simple script that I frequently use in jQuery to make boxes equal heights. Now, I need to convert it to mootools for a new project where the boxes will be floated left at thirds using style sheets. <div id="box1" class="equals">content he ...

What is the process for including an SVG file in my JavaScript .textContent?

I've been struggling to add an exclamation SVG to this section, but for some reason I can't make it work. I've searched on Google for a solution, but haven't found one yet. The SVG file is already downloaded and stored in my project fo ...

Remove the hyphen from a user input field using Angular 2 and reactive forms

After deleting data from input fields, I am facing an issue where the dynamic addition of a hyphen prevents the input field from being cleared. Is there a solution to this problem? How can I delete or clear the input fields effectively? Although I have ad ...

Trigger a function when <a> is clicked, which will then increment the count by one and reflect this change in the database

The database generates the content of this div ordered by a count number called "cts". Whenever I click on the div, I want the "cts" number to increase by one, and then update the change in the database. This will result in the content changing accordingly ...

Obtain template from AngularJS and transmit data to template

<section ng-app="app" ng-controller="ctrl"> <div id="output">{{ foo }}</div> <button ng-click="myFun()">Click me</button> </section> var app = angular.module("app", []); app.controller('ctrl', funct ...

Distinguishing between a regular JavaScript variable and one annotated with a dollar sign

Many responses have addressed the question of using a dollar sign in JavaScript variables. In essence, the dollar sign functions as an identifier in JavaScript variables. However, I am curious if there are other distinctions between regular variables and ...