0

I want to make multiple calls for which I have to wait for the answer, and afterwards I want to group all responses in an array. I've not succeeded to do this.

The res constant in code below still retains the array of promises, not their results. I have no idea what else to try. No other stackoverflow answers have been helpful.

What I've tried:

const getProjectData = async (projectID) => await callAPI(`/projects/${projectID}`);

  const solve = async () => {
    const projects = [];
    currentUserProjectIDs.forEach((project) => {
      projects.push(getProjectData(project));
    });

    console.log("projects", projects);

    const res = await Promise.all(projects);
    console.log("solve res", res);
    return res;
  };

  const res = await solve();
  console.log("res", res);

Below is the result of the last console log:


res [
  Response {
    size: 0,
    timeout: 0,
    [Symbol(Body internals)]: { body: [PassThrough], disturbed: false, error: null },
    [Symbol(Response internals)]: {
      url: 'http://localhost:4000/projects/1',
      status: 200,
      statusText: 'OK',
      headers: [Headers],
      counter: 0
    }
  },
  Response {
    size: 0,
    timeout: 0,
    [Symbol(Body internals)]: { body: [PassThrough], disturbed: false, error: null },
    [Symbol(Response internals)]: {
      url: 'http://localhost:4000/projects/4',
      status: 200,
      statusText: 'OK',
      headers: [Headers],
      counter: 0
    }
  }
]

callAPI function:

export const callAPI = async (path, body, method = "GET") => {
  const config = {
    method: method,
    headers: {
      "Content-Type": "application/json",
    },
  };
  if (body) {
    config.body = JSON.stringify(body);
  }
  const URL = `${process.env.HOST}${path}`;
  return await fetch(URL, config);
};

EDIT: I have tried another way, but still unsuccessful. In the code below, the console.log inside the second .then() logs the correct data, but the returned prj is still an empty array...

const solve = async () => {
    const projects = [];
    currentUserProjectIDs.map((p) => {
      callAPI(`/projects/${p}`)
        .then((r) => {
          return r.json();
        })
        .then((a) => {
          console.log("a", a);
          projects.push(a);
          return a;
        });
    });
    return projects;
  };

  const prj = await solve();
  console.log("prj", prj);
gusti
  • 393
  • 4
  • 21
  • Sounds peculiar, your code looks like it should work. Can you edit a live Stack Snippet into the question that demonstrates the problem? (you could implement `callAPI` with something like `Promise.resolve`) – CertainPerformance Dec 03 '22 at 18:37
  • 1
    Your output is not an array of promises, it's an array of Response objects. – Evert Dec 03 '22 at 18:46
  • @Evert well, okay, but how can I get my data out of them? – gusti Dec 03 '22 at 18:50
  • Iterate the array – Daniel A. White Dec 03 '22 at 18:50
  • @DanielA.White ehm, how? The elements of the array are far from what I get when making a single `callAPI` call. What I have here is not the JSON I need. But I presume there is a way of extracting the actual JSON data from the response. Could you help me with this? – gusti Dec 03 '22 at 18:55
  • "*The res constant in code below still retains the array of promises*" - no. It contains an array of `Response` objects. – Bergi Dec 03 '22 at 21:39

3 Answers3

2

Your edit is almost correct, but you have to use the array returned from .map and include the Promise.all from your original post:

const solve = async () => {
   const projects = currentUserProjectIDs.map((p) => {
     return callAPI(`/projects/${p}`)
        .then((r) => {
          return r.json();
        });
    });
    return Promise.all(projects);
  };

  const prj = await solve();
  console.log("prj", prj);

Pushing to a separate array won’t work because the code in .then is executed asynchronously.

You will probably also want to handle errors with .catch, I assume you omitted that for brevity.

helloitsjoe
  • 6,264
  • 3
  • 19
  • 32
  • Neither of these work. The first pushes to the `projects` asynchronously, the second returns `undefined` from the `map` callbacks. – Bergi Dec 03 '22 at 21:43
1

This is a bit cleaner solution based on your first attempt:

export const callAPI = async (path, body, method = 'GET') => {
  const config = {
    method: method,
    headers: {
      'Content-Type': 'application/json'
    }
  }

  if (body) {
    config.body = JSON.stringify(body)
  }

  const URL = `${process.env.HOST}${path}`
  const res = await fetch(URL, config)

  if (!res.ok) throw new Error('Error fetching data')

  return res.json()
}

const getProjectData = (projectID) => callAPI(`/projects/${projectID}`)

const solve = () => {
  const projects = currentUserProjectIDs.map((project) => {
    return getProjectData(project)
  })

  return Promise.all(projects)
}

solve()
  .then((data) => console.log(data))
  .catch((err) => console.error(err))

The problem was (before your first edit) that you never called the json() method of the response, that's why you were getting an array of Response objects.

Let me know if that helps.

ivanatias
  • 3,105
  • 2
  • 14
  • 25
0

You can do so:

  const getProjectData = async (projectID) => await callAPI(`/projects/${projectID}`);
    
  const solve = async () => {
    const res = await Promise.all(
     currentUserProjectIDs
       .map(id => getProjectData(id)
       .then(res => res.json())
   );
    return res;
  };
  const res = await solve();
  • Better move the `.json()` call right inside the `getProjectData` function. And don't forget to check the status of the response! – Bergi Dec 03 '22 at 21:41
  • @Bergi, thanks for your remark, it makes sense. I just wanted to show the reason why he is not given an array of results. – Alexandr Chazov Dec 04 '22 at 05:27