Rodrigo
Rodrigo

Reputation: 94

Wait for a fetch function to finish

I'm trying to use the curseforge API in a project using fetch in nodejs18, this is the code I'm using:

ids = ["238222","60089","556448"]

const headers = {
  'Accept':'application/json',
  'x-api-key':'API KEY'
};

function getMods(id){
  fetch("https://api.curseforge.com" + '/v1/mods/' + id,
  {
    method: 'GET',

    headers: headers
  })
  .then(function(res) {
      return res.json();
  }).then(function(body) {
      console.log(body.data.name);
  });
}

ids.forEach(element => {
  getMods(element)
});

//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------

console.log("download finished")

With that code what you want to be printed in the terminal is:

Alex's Delight
Mouse Tweaks
Just Enough Items (JEI)
download finished

but when running the program I get this in the terminal:

download finished
Alex's Delight
Mouse Tweaks
Just Enough Items (JEI)

This happens because the fetch function is asynchronous, I have tried all the methods to solve this problem but no solution is what I want.

What I want is for the program to wait for the foreach and fetch to finish to continue executing.

Upvotes: 0

Views: 1792

Answers (3)

kennarddh
kennarddh

Reputation: 2665

Since you run nodejs 18 you can use top level await

const ids = ["238222", "60089", "556448"]

const headers = {
  'Accept': 'application/json',
  'x-api-key': 'API KEY'
};

async function getMods(id) {
  await fetch("https://api.curseforge.com" + '/v1/mods/' + id, {
      method: 'GET',

      headers: headers
    })
    .then(res => res.json())
    .then(body => {
      console.log(body.data.name);
    });
}

try {
  await Promise.all(ids.map(id => getMods(id)))

  console.log("download finished")
} catch {
  // Handle error

  console.error("download error")
}

Upvotes: 0

Evert
Evert

Reputation: 99533

Here's roughly how it's done:

const ids = ["238222","60089","556448"]

const headers = {
  'Accept':'application/json',
  'x-api-key':'API KEY'
};

async function getMods(id){

  const res = await fetch("https://api.curseforge.com" + '/v1/mods/' + id, {
    method: 'GET',
    headers,
  })
  const body = await res.json();
  console.log(body.data.name);
}

async function run() {

  for(const element of ids) {
    await getMods(element);
  }
  console.log("download finished")

}

run();

If you want to download the mods in parallel instead of one at a time, this is how the run() function should look like instead:

async function run() {

  await Promise.all(
    ids.map(element => getMods(element))
  );
  console.log("download finished")

}

If you use ESM, you can avoid the 'run' function and just use await at the top-level. To easily take advantage of that with node, just save your file with the .mjs extension.

Then the final code might look like this:

const ids = ["238222","60089","556448"]

const headers = {
  'Accept':'application/json',
  'x-api-key':'API KEY'
};

async function getMods(id){

  const res = await fetch("https://api.curseforge.com" + '/v1/mods/' + id, {
    method: 'GET',
    headers,
  })
  const body = await res.json();
  console.log(body.data.name);
}


await Promise.all(
  ids.map(element => getMods(element))
);
console.log("download finished")

Upvotes: 1

Stanley Ulili
Stanley Ulili

Reputation: 1364

Something like this can work:

...
promisesArray = []
ids.forEach(element => {
  promisesArray.push(getMods(element))
});

Promise.all(promisesArray).then(() => { 
   console.log("download finished");
 });

Upvotes: 0

Related Questions