How to Use Fetch Within a For-Loop, Wait for Results and Then Console.Log It

Javascript - How to use fetch inside a for loop and wait for results

Possible duplicate of: How to use fetch within a for-loop, wait for results and then console.log it

You need to make the function that starts the loop as async, then you can use await to wait for fetch completion in the subsequent calls without having to use then.

In your case, it'll be something like:

fetch("______", {})
.then((response) => {
return response.json();
})
.then(async (data) => {
for (var i = 0; i < data.length; i++) {
//...

const res = await fetch("_______" + data[i].d, {});
const depData = await res.json();

dep_span.innerHTML = depData.name;
//...
}
});

Working example: https://jsfiddle.net/9sdwefyv/34/

fetch json in a For loop run after the loop is completed JS

If I understand correctly, you are trying to have something execute after all your fetching completes. My favorite way to do this is using Promises and the async await syntax. I rewrote your code trying to flatten things out, and it also will now wait until the loops are done before logging "loop end".

The first thing I do is wrap my entire action in an anonymous async function that is immediately called.

The reason I use async, is because it is needed to use await. Now instead of using .then chains, I can tell the code to wait for the fetch to complete before continuing. This works on not only the fetch which returns a promise, but the .json method that returns a promise as well.

The for of loop is similar to yours, but I use the const keyword to prevent changing the value of a global variable.

I assume there is always an id, and would have used id actions without the need of checking to see if there is an id.

Instead of going through each key and having an if statement which executes code when a certain key is found, I loop through the desired targets and skip executing the code if the key does not exist.

If you need to do something different for any of the keys, you can remove them from the loop and just repeat it, but it gets pretty wet. Anything that is repeated should be put into a function or loop to only be written once

If you have any questions please ask!

(async () => {
const response = await fetch(url);
const data = await response.json();
for (const item of data.list) {
//do something with id?
for (const key of ["name", "address", "city"]) {
if (!(key in item)) continue;
const keyRes = await fetch(`${url}/${item[key]}`);
const keyData = await keyRes.json();
//do something
console.log(`fetch item: ${key}`);
}
}
console.log("loop end");
})();

You mentioned that the fetches are taking too long happening in order, to give you an example of doing this without blocking in the loop, but blocking until all loop promises resolve, here is another example.

Here we write an async function processKey to handle the processing with async await syntax. Async function always return a promise, and the await keyword will wait for the promise to resolve. This way, we can call the function, but instead of waiting for it to resolve, we can store the promise in an array.

After we initiate all the promises without waiting for them to resolve, we can then use await Promise.all(promisesToWaitFor) and wait until all the promises in the array resolve before executing the last line of code.

It should be pointed out that we are saving time by no longer waiting for code in the loop to execute. Because of this, if one fetch takes less time than another, we will have actions in the loop happening "out of order". Depending on your use case, this may or may not be ideal.

If you need any clarification of the code, please ask!

async function processKey(key, url, item) {
const keyRes = await fetch(`${url}/${item[key]}`);
const keyData = await keyRes.json();
//do something
console.log(`fetch item: ${key}`);
}
const url = "Something";
(async () => {
const response = await fetch(url);
const data = await response.json();
const promisesToWaitFor = [];
for (const item of data.list) {
//do something with id?
for (const key of ["name", "address", "city"]) {
if (!(key in item)) continue;
const processPromise = processKey(key, url, item);
promisesToWaitFor.push(processPromise);
}
}
await Promise.all(promisesToWaitFor);
console.log("loop end");
})();

EDIT:

You mentioned that you wanted to keep things in order. I decided instead of preforming all the actions side by side, we could store all the .json promises into an array, then after they have all been initiated, step through that array waiting for each one to finish. This keeps the order of things while still getting some better speed.

One thing I wanted to clarify was combining async/await with promise chaining .then method.

I have the line

const jsonPromise = fetch(`${url}/${item[key]}`).then(res => res.json());

This will use the promise chain to trigger the action of fetching and converting to json in one line. I can store that Promise to convert to json in a variable, and then store that in an array. Since the action we wanted to preform with that data involved the key used to get the data, I pass both the promise and the key to an object in the array.

After we trigger all the fetching, then I have a separate loop which uses await to wait for the action to complete.

(async () => {
const response = await fetch(url);
const data = await response.json();
const fetchToWaitFor = [];
for (const item of data.list) {
//do something with id?
for (const key of ["name", "address", "city"]) {
if (!(key in item)) continue;
const jsonPromise = fetch(`${url}/${item[key]}`).then(res => res.json());
fetchToWaitFor.push({jsonPromise, key});
}
}
for (const waitObj of fetchToWaitFor) {
const {key, jsonPromise} = waitObj;
const data = await jsonPromise;
console.log(`fetch item: ${key}`);
}
console.log("loop end");
})();

Fetch in fetch inside a loop JS

You can solve it using Promise all.

let promises = [];
for (let i = 1; i <= 300; i++) {
promises.push(fetch(`example.api/incomes/${i}`));
}
Promise.all(promises)
.then(function handleData(data) {
return fetch("example.api") // should be returned 1 time
.then(response => {
if (response.ok) return response.json();
throw new Error(response.statusText);
});
})
.catch(function handleError(error) {
console.log("Error" + error);
});

Wait for for loop to complete before continuing

fetch is asynchronous. Anything after your fetch block is not dependent on what is happening in there to complete.

You have a couple of options.

Option 1: Move console.log('loop is finished!'); to call back in the second then method.

fetch('http://localhost:3000/pet/'+pet)
.then(res => res.json())
.then(data => {
const newData = (data);
for (var x in newData) {
finalpetName.push(newData[x].petName);
console.log('pet' + x + 'is retrieved');
};
console.log('loop is finished!');
})
.catch(err => {
console.error(err)
});

Option 2: Use finally if you want it to execute after all the asynchronous work is done, including handling any errors

fetch('http://localhost:3000/pet/'+pet)
.then(res => res.json())
.then(data => {
const newData = (data);
for (var x in newData) {
finalpetName.push(newData[x].petName);
console.log('pet' + x + 'is retrieved');
};
})
.catch(err => {
console.error(err)
})
.finally(() => console.log('loop is finished!'));

Doing fetch statements inside loop javascript async-await

I recommend you don't mix .then's and awaits; use one or the other. Mixing the coding styles makes it much harder to keep track of order of execution and what will what on what. The 12 lines starting with fetchBlob(src) are creating a promise, but the don't actually wait for it.

Instead, you should change those lines to:

const blob = await fetchBlob(src);
let file = new File([blob], "new_file");
const json = await fetchUrl(file);
console.log('image');
if (isJson(json)) {
let obj = JSON.parse(json);
if (obj.status == '200') {
tmp.querySelectorAll('img')[i].src = obj.url;
}
}

As a reminder, the await keyword can only be used in an async function, so make sure the surrounding function is an async function.

With those modifications, the for loop will wait before moving to the next value of i, and will thus wait before getting to console.log('execute html')

How to wait for secondary fetched result with JavaScript when the result of a primary fetch is a specific value within a loop?

Using .then() syntax on a conditional promise:

json => {
const field = getFieldByName(json, "power_delivered_l");
const myPhase = field.name.replace('power_delivered_l','');
(field.value == 0 // check if power is generated
? fetch(APIGW+"v1/sm/fields/power_returned_l"+myPhase)
.then(response => response.json())
.then(json2 => getFieldByName(json2, "power_returned_l", -1))
: Promise.resolve(field)
).then(({name, value: nvKW}) => {
console.log(`${name} = ${nvKW}`)
document.getElementById(json.fields[j].name).innerHTML = nvKW.toFixed(1);
// a lot more ui stuff is done below
});
}

Using async/await:

async json => {
let field = getFieldByName(json, "power_delivered_l");
if (field.value == 0) // check if power is generated
const myPhase = field.name.replace('power_delivered_l','');
const response = await fetch(APIGW+"v1/sm/fields/power_returned_l"+myPhase);
const json2 = await response.json())
field = getFieldByName(json2, "power_returned_l", -1))
}
const {name, value: nvKW} = field;
console.log(`${name} = ${nvKW}`)
document.getElementById(json.fields[j].name).innerHTML = nvKW.toFixed(1);
// a lot more ui stuff is done below
}

Both using a helper function

function getFieldByName(json, prefix, factor = 1) {
for (const field of json.fields) {
if (field.name.startsWith(prefix)) {
return {
name: field.name,
value: factor * Number(field.value)
};
}
}
throw new Error(`Did not find field '${prefix}' in the JSON`);
}

how to wait for all requests to complete before doing the next action (fetch)

I would suggest using async-await as it makes code cleaner. Async-await is just an alternaitive to using promises and is many times considered a better choice. I have converted your function into arrow function. The implementation is given below: