I am trying to work with an api where I have to send a request for each item in a list.
However, I see that the loop doesn't seem to wait for every request, i.e, the loop doesn't work as expected. Here's the code below
getInfo = async () => {
const mylist = ["item1","item2","item3","item4","item5","item6","item7"]
const responses = []
const len = mylist.length
for (let i = 0; i < len; i++) {
//console.log("inside loop")
await axios.get("some_url/"+mylist[i])
.then(res => {
responses.push(res.data)
})
}
When I run the program, all the console.log("inside loop") executes immediately without waiting for the request to be complete.
How can I modify the code so as to wait for each response to be completed before updating the for loop counter variable?
You could try re-arranging the code to something like this. But using a Promise.all with Array.prototype.map would be more idiomatic solution for the problem.
await the async call (remove unnecessary .then call) and then console.log
getInfo = async () => {
const mylist = ["item1","item2","item3","item4","item5","item6","item7"]
const responses = []
const len = mylist.length
for (let i = 0; i < len; i++) {
responses.push((await axios.get("some_url/"+mylist[i])).data)
console.log("inside loop")
}
}
Internally, await is translated into a Promise chain. Since the for loop can't be transformed into a Promise-continuation, you'll need to convert it to a Promise-based construct.
Depending on what you want to achieve there are multiple ways to go about it.
Constructing the responses array could be done with a map statement.
const promises = mylist.map(item => {
return axios.get("some_url/"+item).then(res => { return res.data; })
});
const data = await Promise.all(promises);
No manual pushing items around or fiddling with the array length.
Related
When I run this code it give me a random order of Pokémons. I don't know where is the problem.
Thank you so much.
for (var i = 1; i <= 20; i++) {
apiPokemon("https://pokeapi.co/api/v2/pokemon/"+i);
async function apiPokemon(urlPokemon) {
const response = await fetch(urlPokemon);
const dataPokemon = await response.json();
var id = dataPokemon.id;
var name = dataPokemon.name;
console.log(id, name);
}
}
First thing's first: "Why are they coming back in random order?" - Because you are not awaiting each response. Instead you are firing off all 20 async calls which can come back in any order, so that's why they are logging in a random order.
In order to fix this, there are a few changes I'd recommend:
Extract your apiPokemon function out of your loop so it doesn't get recreated for each loop iteration
Return the entire data object from your apiPokemon function
Add all of the apiPokemon requests to an array and await them with Promise.all()
Log the output of the Promise.all() and you'll see that they will now always be in correct order
async function apiPokemon(urlPokemon) {
const response = await fetch(urlPokemon);
const dataPokemon = await response.json();
return dataPokemon;
}
async function getPokemon(startIndex, stopIndex) {
let requests = [];
for (let i = startIndex; i <= stopIndex; i++) {
requests.push(apiPokemon("https://pokeapi.co/api/v2/pokemon/"+i));
}
let pokemonList = await Promise.all(requests);
for (let pokemon of pokemonList) {
console.log(pokemon.id, pokemon.name);
}
}
getPokemon(1, 20)
I have a loop that gets iterated through 1000 times where each iteration makes a request and then prints the result of that request.
Similar to below.
let start = console.log(Date.now())
for (i = 0; i < 1000; i++) {
request.then(data => {
console.log(Date.now() - start)
})
}
This however results in the first request taking much longer to be completed then if there was just 1 iteration of the loop.
with i < 1000:
5860
...
with i < 1:
220,
...
For the aim of this script however, I want to wait for each result to be received before starting the next iteration of the loop.
If you want to stick with the ES5 way of handling Promises, you could use the following approach:
const start = console.log(Date.now())
// Create a instantly resolved promise
for (let i = 0, p = Promise.resolve(); i < 1000; i++) {
// append new promise to the chain
p = p.then(() => request())
.then(() => console.log(Date.now() - start));
}
If you can use ES6 methods, you could write this using the async/await pattern like so:
const asyncLoop = async () => {
const start = console.log(Date.now())
for (let i = 0; i < 1000; i++) {
const data = await request();
console.log(Date.now() - start);
}
}
asyncLoop();
The chance that you really want to make the requests one after the other is actually pretty small though, so in case you want to make the request simultaneously, but do something after all of them resolved, you can use Promise.all(...)
const start = console.log(Date.now())
const requests = [request1, request2, ...];
Promise.all(requests)
.then(() => console.log(Date.now() - start));
You can use the async-await pattern here.
You can achieve this by changing the code
async function iteration() {
let start = console.log(Date.now())
for (let i = 0; i < 1000; i++) {
const data = await httpRequest()
console.log(Date.now() - start)
}
}
async function httpRequest() {
return new Promise((resolve, reject) => {
request.then(data => {
//Do anything you want to do with data
resolve(data)
}).catch(error => {
console.error(`Error in request`)
reject(error)
})
})
}
Explanation:
I have moved the request code in a separate function httpRequest and this function will return promise on success
I called httpRequest in for loop using await keyword, now loop will wait till the request is completed
I have also wrapped for loop code in function since you can only use await inside an async function
Having a for loop inside a promise. How can i get response from getData API without using async await. The parameters used inside getData are coming from for loop.
var res = EService.webApi.get.GetActiveData(formModel.project.EID);
res.then(
async function (result) {
//success
var data = result.data;
var eList= data.BodyData;
var jList= [];
for (var i = 0; i < eList.length; i++) {
let entity = await getData(eList[i].EntityID);
if (eList[i].typeID !== 16) {
jList.push({
Name: eList[i].Name + " - " + e[i].typeName + " - " + entity.Name,
EID: eList[i].EntityID,
model: eList[i],
});
}
}
}
If I understand what you're asking, it sounds like you want to invoke all of the async requests right away but have each one await its result, rather than invoking them in serial where each one awaits the previous operation. Maybe something like this:
for (var i = 0; i < eList.length; i++) {
(async (j) => {
// The rest of your logic, but using the passed `j` instead of `i`
})(i);
}
The anonymous async function isn't awaited, but internally each call to that function can await the call to getData to use its result.
Though if you want to do something with jList or any other result/side-effect afterward then you'd need to await the whole thing. Maybe put all of the promises into an array and await the array. Perhaps something like:
let promises = [];
for (var i = 0; i < eList.length; i++) {
promises.push((async (j) => {
// The rest of your logic, but using the passed `j` instead of `i`
})(i));
}
Promise.all(promises).then(() => ...);
The overall goal being that all of the operations run in parallel, rather than in serial like in the original loop.
This question already has answers here:
Resolve promises one after another (i.e. in sequence)?
(36 answers)
Closed 1 year ago.
I'm trying to resolve promises one by one, but i don't understand how to. I want to wait till video message load fully then move to another promise, otherwise it freeze browser. In my code example it don't wait until video hit loadedmetadata event, and go to next promise. And after all promises executed, load's all videos. How it looks: https://prnt.sc/10mg4oc
//All messages loop
for (var i = 0; i < data[0].length; i++) {
let urls = [];
$.each(file_id.split(','), function( index, value ) {
file_id = value;
urls.push("//localhost/storage/"+data[0][i].user_id+"/"+data[0][i].file_cluster_id+"/"+value)
});
//Get chunks array from file cluster
let chunkPromises = urls.map(function callback(url,index) {
return fetch(url).then((res) => {
return res.arrayBuffer();
});
});
console.log(chunkPromises);
console.log(0);
//I'm trying to understand how to do it
(async function loop() {
await Promise.all(chunkPromises).then(chunks => {
console.log(chunks);
console.log(1);
//Appending file chunks to file back
for (const value of chunks) {
console.log(2);
}
video.attr('src', URL.createObjectURL(full_video));
console.log(3);
//I want that promise complete loadedmetadata, then go to another promise iteration
video.on("loadedmetadata", function () {
console.log(`${secondsReady} seconds of video are ready to play.`);
});
});
});
}
If it possible i woold like, not touch all messages loop.
You can actually use async/await in for loops, it doesn't work well with other forloop iteration like forEach, filter etc. :
const PromiseMe = () => {
return new Promise((res, rej) => {
setTimeout(() => {
res('Integrate Me');
}, 1000)
});
}
const main = async () => {
for(let i = 0; i< 10; i++) {
let val = await PromiseMe();
console.log(val);
}
}
main();
So, you can actually implement this in you for-loop like this.
const result = [];
const mockFunction = async () => {
for (var i = 0; i < data[0].length; i++) {
//Get chunks array from file cluster
//urls is an array
let chunkPromises = await fetch(url[i]);
result.push(chunkPromises); //parse this data to specific format if needed
}
console.log(result);
I have an array of Objects/String.
this.addtenant().subscribe(r => {
let a = ['plant1', 'plant2'];
for (let b of a) {
this.myService.saveAllData(b).subscribe(r => {
console.log("result" r);
});
}
});
and getAllData is in myservice file which returns an Observable.
saveAlldata(b) {
this.http.post(url,b);
}
The problem is since i am using subscribe the call is being asynchronus, i want to have it something like:
first "plant1" post call has to finish, and then plant2 has to be made. In simple words synchronus call.
I think you should use async/await for synchronous calls.
Here is one of the tutorial:
https://www.techiediaries.com/javascript-async-await-tutorial/
A sample code demo would be something like:
responseArr: any[] = [];
func1()
let x = [1,2,3];
func2(x);
}
async func2(arr) { // make the function async
for(let i=0; i<arr.length ; i++){
const response: any = await this.myService(arr[i]); // wait on each service call. This will give synchronous behaviour
this.handleResponse(response); // handle response in a function if there is common functionality in each response
}
reuturn responseArr; // return the final response
}
handleResponse(response) {
responseArr.push(response);
}
I have found a solution by myself. You can use an async await inside a loop, since forEach is not promise aware and cannot be used with Promise. Here is the final code:
this.addtenant().subscribe(async r => {
let a = ['plant1', 'plant2'];
for(let index=0; index=a.length; index++)
await this.myService.saveAllData(b).toPromise().then(r => {
console.log("result" r);
});
}
}
});
As await works with Promise, so toPromise will replace subscribe()