Home > Back-end >  How to use two map function with two fetching url
How to use two map function with two fetching url

Time:10-02

I have two api that I should turn them array like that

[
            {
                key: "0",
                label: 'Installation',
                children: [
                    { key: "0-0", label: 'Getting Started', url: 'https://reactjs.org/docs/getting-started.html' },
                    { key: "0-1", label: 'Add React', url: 'https://reactjs.org/docs/add-react-to-a-website.html' },
                    { key: "0-2", label: 'Create an App', url: 'https://reactjs.org/docs/create-a-new-react-app.html' },
                    { key: "0-3", label: 'CDN Links', url: 'https://reactjs.org/docs/cdn-links.html' }
                ]
            },
            {
                key: "1",
                label: 'Main Concepts',
                children: [
                    { key: "1-0", label: 'Hello World', url: 'https://reactjs.org/docs/hello-world.html' },
                    { key: "1-1", label: 'Introducing JSX', url: 'https://reactjs.org/docs/introducing-jsx.html' },
                    { key: "1-2", label: 'Rendering Elements', url: 'https://reactjs.org/docs/rendering-elements.html' },
                    { key: "1-3", label: 'Components and Props', url: 'https://reactjs.org/docs/components-and-props.html' },
                    { key: "1-4", label: 'State and LifeCycle', url: 'https://reactjs.org/docs/state-and-lifecycle.html' },
                    { key: "1-5", label: 'Handling Events', url: 'https://reactjs.org/docs/handling-events.html' }
                ]
            }
        ];

By the way, I have two API, one of them get all categories, the second API is to take the names of the children. So I created chained fetch, but I can not take the children object correctly. children array turns that 'children: Promise {: Array(11)}' What does fullfilled mean here? because my code not working due to that reason. I want to share my code also

 getCategories() {
    return fetch('https://challenge.fnaghshin.com/api/cat')
      .then((res) => res.json())
      .then((d) => {
        return Promise.all(
          d.map((e) => {
            return {
              key: e.id,
              label: e.name,
              date: e.created_at,
              children: fetch(
                `https://challenge.fnaghshin.com/api/catpro/${e.id}`
              )
                .then((res) => res.json())
                .then((d) =>
                  d.map((e) => {
                    return {
                      key: e.id,
                      label: e.name,
                    };
                  })
                ),
            };
          })
        );
      });
  }

I also used Promise.all() to wait all promises fullfilled than turn me right array.But I failed

CodePudding user response:

The cleanest solution IMO is to use async/await, with nested Promises it quickly gets fairly unreadable.

async function getCategories() {
  const response = await fetch("https://challenge.fnaghshin.com/api/cat");
  const categories = await response.json();

  const childrenResponses = await Promise.all(
    categories.map((cat) =>
      fetch(`https://challenge.fnaghshin.com/api/catpro/${cat.id}`)
    )
  );

  const childrenData = await Promise.all(
    childrenResponses.map((response) => response.json())
  );

  return categories.map((cat, index) => ({
    key: cat.id,
    label: cat.name,
    date: cat.created_at,
    children: childrenData[index].map((child) => ({
      key: child.id,
      label: child.title
    }))
  }));
}

So first you get your categories, that's pretty simple. Then what you want to do is to use Promise.all() on list of fetch calls with IDs you have from categories (childrenResponses). Then you have to do the same because of .json() in fetch (it would be even cleaner with e.g. axios or similar higher level library instead of fetch), and once you have categories and childrenData you just have to map it the way you want. You can make it a bit more concise by doing e.g. this instead:

const categories = await fetch(
  "https://challenge.fnaghshin.com/api/cat"
).then((res) => res.json());

although in my opinion it's less readable. You should probably also do some error handling, e.g. with try/catch.

CodePudding user response:

Here's a promise based version. I split up the main function into parts, so it's easier to read.

Do note that because you are makeing many requests to the API, you are likely to get a 429: too many requests warning.

To solve that you can either use proxies for each request, or you can check the status code and wait for the time the response tells you to wait.

const URL = 'https://challenge.fnaghshin.com/api/cat';
const CHILDREN_URL = `https://challenge.fnaghshin.com/api/catpro/`;

function getCategories() {
  return fetch(URL)
    .then(res => res.json())
    .then(data => Promise.all(data.map(getSubCategories)));
}

function getSubCategories(data) {
  return fetch(CHILDREN_URL   data.id)
    .then(res => res.json())
    .then(info => ({
      key: data.id,
      label: data.name,
      date: data.created_at,
      children: info.map(e => ({
        key: e.id,
        label: e.title,
      })),
    }));
}

getCategories()
  .then(console.log)
  .catch(console.log)

CodePudding user response:

Promise.all() expects an array of promises, but you were returning an array of objects. The promise was nested in the children prop of the objects. So what we need to do, is map over these objects and return the nested prop children and feed that to Promise.all()

I added an async funtion to the second then block, so I can use await with Promise.all(). That causes the execution of the code to wait until all promises, that is all children have resolved. After that I can simply return the result.

function getCategories() {
  return fetch('https://challenge.fnaghshin.com/api/cat')
    .then((res) => res.json())
    .then(async (d) => {
      const withChildren = d.map((e) => {
        return {
          key: e.id,
          label: e.name,
          date: e.created_at,
          children: fetch(`https://challenge.fnaghshin.com/api/catpro/${e.id}`).then(res => res.json())
        }
      })
      await Promise.all(withChildren.map(item => item.children))
      return withChildren
    })
}

The 429 (Too Many Requests) seems to be a rate limit, when you fire the requests too often within a certain time. I had it, too. Now it is gone again.

PS: Don't use await in map() in this case, as this makes the execution last longer. Because in each iteration of the map, it will await the response of the fetch(), before going to the next iteration and firing the next fetch(). So instead of firing them all at once (basically), you fire them one after another has resolved. It's faster to fire them all at once, and then await Promise.all().

CodePudding user response:

Try this way: much cleaner and easy to control

async function getCategorie() {
  const data = await fetch('https://challenge.fnaghshin.com/api/cat')
  const api1 = await data.json()

  const cats = await Promise.all(
    api1.map(async (e) => {
        return {
          key: e.id,
          label: e.name,
          date: e.created_at,
          children: await fetch(
            `https://challenge.fnaghshin.com/api/catpro/${e.id}`
          ).then(read => read.json())}
      })
  )
  console.log("finally",cats)
  return cats
}

getCategorie()
  • Related