Promise.all with for loop in node.js - javascript

I collect the information of each page from 1 to 10 as API in node.js.
Now I use this code.
async function myWork() {
let results = []
let tmp
let param
for (i=1; i<11; i++) {
param = {'page': i}
tmp = await callMyApi(param) // return a list
results.push(...tmp)
}
return results
}
In this case, each callMyApi behaves like sync.
But I don't care about page order.
So, to speed it up, I want to use something like promise.all to process it in parallel.
How can I use promise.all in for loop in this case?

You can use Promise.all() with concat().
async function myWork() {
let results = [];
let promises = [];
let param;
for (i=1; i<11; i++) {
let param = {'page': i}
let tmpPromise = callMyApi(param);
promises .push(tmpPromise);
}
//promises is now an array of promises, and can be used as a param in Promise.all()
let resolved = await Promise.all(promises);
//resolved is an array of resolved promises, each with value returned by async call
let indivResult = resolved.forEach(a =>
results = results.concat(a));
//for each item in resolved array, push them into the final results using foreach, you can use different looping constructs here but forEach works too
return results;
}

Example below:
async function myWork() {
let results = [];
let param;
for (i = 1; i < 11; i++) {
param = { page: i };
results.push(callMyApi(param));
}
const res = await Promise.all(results);
return res.flat();
}

Related

Order Pokémons from 1 to 20 not a random order

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)

How to make an API call inside for loop, withour using async await

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.

Javascript await inside a loop

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.

Async/Await in javascript for loop

I have a react component that runs this function on the mounting of the component.
function getListOfItems(){
let result = [];
for(let i=0 ; i<5 ; i++){
/**
* code to assign values to some variables namely param1,param2
*/
getDetails(param1,param2);
}
const getDetails = async (param1,param2) => {
let list = await getAPIresults(param1)
result.push(list);
if(result.length === 5){
//code to update a hook which causes render and displays the text in results array
}
}
}
useEffect(() => {
getListOfItems()
},[])
So the code is running but the results array has data in random order. For instance, the results array might look something like this [2,5,1,3,4] where I expect it to be like this [1,2,3,4,5] This means the above code is not running async tasks in the order of their arrival. So could someone help me out to fix this, I want the code to make async requests in order of their arrival.
You need to use the await keyword again to wait for each iteration of the loop to complete before it moves on to the next go-round.
await getDetails(param1,param2)
But as you can only do this in an async function, your getListOfItems will also need to be an async function.
async function getListOfItems(){
let result = [];
for(let i=0 ; i<5 ; i++){
await getDetails(param1,param2);
}
const getDetails = async (param1,param2) => {
let list = await getAPIresults(param1)
result.push(list);
if(result.length === 5){}
}
}
So the code is running but the results array has data in random order.
That's because your loop calls getDetails repeatedly without waiting for the previous call to complete. So all the calls overlap and race.
If it's okay that they overlap but you need the results in order, use Promise.all and have getDetails return its results (rather than pushing them directly).
If you can't make getListOfItems an async function:
const getDetails = async (param1,param2) => {
let list = await getAPIresults(param1)
if(result.length === 5){
//code to update a hook which causes render and displays the text in results array
}
return list;
}
const promises = [];
for (let i = 0; i < 5; ++i) {
promises.push(getDetails(param1, param2));
}
Promise.all(promises)
.then(results => {
// `results` is an array of the results, in the same order as the
// array of promises
})
.catch(error => {
// Handle/report error
});
If you can (and the caller will handle any error that's propagated to it via rejection of the promise from getListOfItems):
const getDetails = async (param1,param2) => {
let list = await getAPIresults(param1)
if(result.length === 5){
//code to update a hook which causes render and displays the text in results array
}
return list;
}
const promises = [];
for (let i = 0; i < 5; ++i) {
promises.push(getDetails(param1, param2));
}
const results = await Promise.all(promises)
// `results` is an array of the results, in the same order as the
// array of promises
If you need them not to overlap but instead to run one after another, your best bet is to use an async function for the loop.
If you can't make getListOfItems an async function:
const getAllResults = async function() {
const results = [];
for (let i = 0; i < 5; ++i) {
results.push(await getDetails(param1, param2));
}
return results;
}
const getDetails = async (param1,param2) => {
let list = await getAPIresults(param1)
if(result.length === 5){
//code to update a hook which causes render and displays the text in results array
}
return list;
}
getAllResults()
.then(results => {
// `results` is an array of the results, in order
})
.catch(error => {
// Handle/report error
});
If you can (and the caller will handle any error that's propagated to it via rejection of the promise from getListOfItems):
const results = [];
for (let i = 0; i < 5; ++i) {
results.push(await getDetails(param1, param2));
}
// Use `results
const getDetails = async (param1,param2) => {
let list = await getAPIresults(param1)
if(result.length === 5){
//code to update a hook which causes render and displays the text in results array
}
return list;
}
You might want to use Promise.all; this would preserve the order as well:
Promise.all: Order of resolved values

Async/Await with jasmine

I am using protractor and Jasmine for testing.
Say I have an async it function.
it('something', async function(){
for(var k = 0; k < 5 ; k++){
(function(jj){
/...........
await something == whatever
............./
})(k);
}
})
i want to use await inside the for loop but I am unable to do so.
Also how can i use async/await to make the code cleaner inside a protractor each function like
it('something', async function(){
element.locator.each((x)=>{
await element.locator.sendKeys('something')
})
})
if anyone can help this would be awesome. As now my whole test spec looks like a big mess of promises and callbacks.
it('description', async function() {
common.separator()
console.log('***************************************')
console.log('something');
console.log('***************************************')
common.separator();
finished_ispresent = await common.finished.isPresent();
if( finished_ispresent == true) {
console.log('finished is present = ' + finished_ispresent);
common.separator();
}
common.somebutton.click();
await browser.sleep(1000);
//array declaration for storing boolean results
some_array =[];
some_array1 =[];
some_array2 =[];
some_array3 =[];
some_array4 =[];
//the outer for loop
for(var k = 0; k < 5 ; k++){
browser.wait(EC.visibilityOf(common.something),2000);
common.something.click();
Something_V = await common.something.get(k).getText();
browser.wait(EC.visibilityOf(common.something),5000);
common.something.click();
common.datepicker(k);
}
});
what happens is things happen so quickly that nothing works and returns a slew of errors.
Sir I have one more doubt regarding .each functions. How do you handle each functions when using async await?
it('something', function(){
element.all.locator.each((element_each)=>{
console.log(element_each);
})
});
How do you handle this with Each?
If you use async/await, no need to use closure and nested await in each.
Let's say there are 5 input boxes on page, we need to input some value for each box, I will show examples of using and non-using async/await
// code non-using await
let values= ['a', 'b', 'c', 'd', 'e'];
it('something', function(){
for(let i=0;i<5;i++) {
(function(j){ // we have to use javascript closure at here
element.all(by.css('input')).get(j).sendKeys(values[j])
})(i);
}
})
// code using await
it('something', async function(){
// you can use count() to get the total input box, rather than hardcode 5
// let cnt = await element.all(by.css('input')).count();
let cnt = 5;
for(let i=0;i<cnt;i++) {
await element.all(by.css('input')).get(i).sendKeys(values[i])
}
})
Two points you should pay attention when use async/await.
1) disable protractor's promise management (control flow) in conf.js
// protractor conf.js
exports.config= {
SELENIUM_PROMISE_MANAGER: false,
}
2) any code line which return promise, you need to add await ahead. otherwise the script execution order will become mess.
it('description', async function() {
common.separator()
console.log('***************************************')
console.log('something');
console.log('***************************************')
common.separator();
finished_ispresent = await common.finished.isPresent();
if( finished_ispresent == true) {
console.log('finished is present = ' + finished_ispresent);
common.separator();
}
await common.somebutton.click();
await browser.sleep(1000);
//array declaration for storing boolean results
some_array =[];
some_array1 =[];
some_array2 =[];
some_array3 =[];
some_array4 =[];
//the outer for loop
for(var k = 0; k < 5 ; k++){
await browser.wait(EC.visibilityOf(common.something),2000);
await common.something.click();
Something_V = await common.something.get(k).getText();
await browser.wait(EC.visibilityOf(common.something),5000);
await common.something.click();
await common.datepicker(k);
}
});
using await in .each
it('using await in each()', async function(){
await browser.get("https://www.npmjs.com/");
element.all(by.css('nav > ul >li >a')).each(async function(item){
var txt = await item.getText();
console.log(txt);
})
})

Categories

Resources