Axios.get().then() in a for loop - javascript

How would I go about running Axios in a for loop, each with a corresponding .then() function. Then after the for loop ends, run another function.
Example:
const array = ['asdf', 'foo', 'bar'];
let users = [];
for (i = 0; i < array.length; i++) {
axios.get('/user/' + array[i].id).then(response => {
// do something with response
users.push(response);
});
}
console.log(users);

const array = [{ id: 'asdf'}, { id: 'foo' }, { id: 'bar' }]; // changed the input array a bit so that the `array[i].id` would actually work - obviously the asker's true array is more than some contrived strings
let users = [];
let promises = [];
for (i = 0; i < array.length; i++) {
promises.push(
axios.get('/user/' + array[i].id).then(response => {
// do something with response
users.push(response);
})
)
}
Promise.all(promises).then(() => console.log(users));
The .then() method of a Promise itself returns a Promise; so you can collect those and await all of them with Promise.all().
Note that even if you're doing this within an async function, you don't want to await inside the for-loop, because then each request will wait for the previous one to finish before it even starts, and presumably you want to run these requests in parallel.
Depending on your use case, a concise async / await function might look like this:
async function getMultiple(...objectsToGet) {
let users = [];
await Promise.all(objectsToGet.map(obj =>
axios.get('/user/' + obj.id).then(response => {
// do something with response
users.push(response);
})
));
return users;
}
// some other async context
console.log(await getMultiple({ id: 'asdf'}, { id: 'foo' }, { id: 'bar' }));

If you are using a more recent version of javascript with async/await support, you can do the following:
const array = ['asdf', 'foo', 'bar'];
let users = [];
for (const id in array) {
const response = await axios('/user/' + id);
users.push(response);
}
console.log(users);

You should collect all the promises inside an array and use promise.all in the following manner -
const array = ['asdf', 'foo', 'bar'];
let promises = [];
for (i = 0; i < array.length; i++) {
promises.push(axios.get('/user/' + array[i].id))
}
Promise.all(promises)
.then(responses => console.log(responses));

Related

iterating Javascript array and delete based on condition

I want to iterate through an array of words, look up the definition and delete the word if no definition is found.
my code looks as follows;
var words = ["word1", "word2", "word3",]
function Meaning(words){
const getMeaning = async () => {
const response = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${words}`)
const myJson = await response.json()
for(i = 0; i < words.length; ++i) {
if(!response[i]){
myJson.splice(i,1)
console.log(myJson)
}
}}
This is not really doing anything atm. Where am I going wrong?
edit to add context
tried like this as well;
for(i = 0; i < words.length; ++i)
fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${words[i]}`).then((response) => {
if (response === 404) {
let response = words
words[i].splice(i,1)
console.log(response)
}
throw new Error('Something went wrong');
})
.then((responseJson) => {
let response = words
response[i].splice(i,1)
})
.catch((error) => {
console.log(error)
});
I can print out the 404 error when it finds no definition, but I can't remove it from the words array
After quick look at the API, and it appears to handle only single words, so the caller needs to make the requests one at a time. Here's how to do it...
const baseUrl = 'https://api.dictionaryapi.dev/api/v2/entries/en/';
// one word lookup. resolve to an array of definitions
async function lookupWord(word) {
const res = await fetch(baseUrl + word);
return res.json();
}
// resolve to a bool, true if the word is in the corpus
async function spellCheck(word) {
const defArray = await lookupWord(word);
return Array.isArray(defArray) && defArray.length > 0;
}
// create a spellCheck promise for every word and resolve with the results
// note, this mutates the array and resolves to undefined
async function spellCheckWords(array) {
const checks = await Promise.all(array.map(spellCheck));
for (let i=array.length-1; i>=0; i--) {
if (!checks[i]) array.splice(i,1);
}
}
// test it (a little)
let array = ['hello', 'whereforeartthou', 'coffee'];
spellCheckWords(array).then(() => {
console.log(array)
})
try this code, you need to check every single element of array from response
var words = ["word1", "word2", "word3"];
function Meaning(words) {
const getMeaning = async () => {
const response = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${words}`)
const myJson = await response.json()
let result = [];
myJson.forEach(element => {
if(words.includes(element)) {
result.push(element)
}
});
return result;
}
return getMeaning();
}

Running forEach on Object.entries does not return the same thing as a for loop

I am iterating over an object using a regular for loop and that works fine for me. But, I was trying to remove all for loops of my code in favor of array iteration instead and, for some reason I can't understand why when using forEach I get a different result.
Note: forEach here is from a module called p-iteration
https://www.npmjs.com/package/p-iteration
This works fine, it returns the correct values.
for await (const [key, value] of Object.entries(tatGroupedByRegion)) {
onTarget = 0;
notOnTarget = 0;
const cases = [];
await forEach(value, async email => {
if (!cases.includes(email.new_name)) {
cases.push(email.new_name);
isOnTarget(email);
}
});
backlogData[key].tatd1 = percentage(onTarget, notOnTarget);
tatd1Total.value += parseInt(percentage(onTarget, notOnTarget), 10);
if ((parseInt(percentage(onTarget, notOnTarget) !== 0), 10)) {
tatd1Total.count += 1;
}
}
This does not work,this part here backlogData[key].tatd1 = percentage(onTarget, notOnTarget), returns the same value over and over.
await forEach(Object.entries(tatGroupedByRegion), async ([key, value]) => {
onTarget = 0;
notOnTarget = 0;
const cases = [];
await forEach(value, async email => {
if (!cases.includes(email.new_name)) {
cases.push(email.new_name);
isOnTarget(email);
}
});
backlogData[key].tatd1 = percentage(onTarget, notOnTarget);
tatd1Total.value += parseInt(percentage(onTarget, notOnTarget), 10);
if ((parseInt(percentage(onTarget, notOnTarget) !== 0), 10)) {
tatd1Total.count += 1;
}
});
exports.forEach = async (array, callback, thisArg) => {
const promiseArray = [];
for (let i = 0; i < array.length; i++) {
if (i in array) {
const p = Promise.resolve(array[i]).then((currentValue) => {
return callback.call(thisArg || this, currentValue, i, array);
});
promiseArray.push(p);
}
}
await Promise.all(promiseArray);
};
This is the implementation of forEach that you're using. The callback receives this as the first argument, this can be a problem.

Why do the last two functions work, but not the first?

I have three different functions that should do the same thing, populate an array with resolved Promises, but it's not working for the first example.
Here's my code:
(async() => {
const items = [];
const someFn = async() => {
const v = await Promise.resolve(10);
items.push(Math.random());
return Promise.resolve(v * 10);
}
const arr = [];
for (let i = 0; i < 10; i++) {
arr.push(someFn);
}
await Promise.all(arr);
console.log("item 1", items);
})();
(async() => {
const items = [];
const someFn = async() => {
const v = await Promise.resolve(10);
items.push(Math.random());
return Promise.resolve(v * 10);
}
const arr = [...Array(10).keys()].map(someFn)
await Promise.all(arr);
console.log("items 2", items);
})();
(async() => {
const items = [];
const someFn = async() => {
const v = await Promise.resolve(10);
items.push(Math.random());
return Promise.resolve(v * 10);
}
for (let i = 0; i < 10; i++) {
await someFn();
}
console.log("items 3", items);
})()
This is the output:
item 1 []
items 2 [ 0.7450904427103939,
0.37106667256699555,
0.12035280341441346,
0.265221052932904,
0.7775494303685422,
0.4872532010723445,
0.6497680191919464,
0.2570485072009576,
0.5613137531648884,
0.95109416178435 ]
items 3 [ 0.25328649499657585,
0.5452758396760038,
0.7274346878509064,
0.9306670111476503,
0.22942578229725785,
0.32547900377461625,
0.9722902638678983,
0.9964743517593542,
0.2828162584401659,
0.7672256760378469 ]
Notice how item 1 is an empty array.
That's because in the first example, someFn is never executed:
for (let i = 0; i < 10; i++) {
arr.push(someFn);
}
await Promise.all(arr);
This part just pushes functions into the arr variable, it doesn't run them, thus not creating Promises and never filling the items array.
On the other hand, the other examples run the function someFn:
const arr = [...Array(10).keys()].map(someFn)
This fills the arr array with 10 executions of someFn (map executes them with the current value (0-9), the index (also 0-9) and the array itself).
for (let i = 0; i < 10; i++) {
await someFn();
}
And this obviously runs someFn in a loop.
To make the first example work, push the result of the function into the array:
(async () => {
const items = [];
const someFn = async () => {
const v = await Promise.resolve(10);
items.push(Math.random());
return Promise.resolve(v * 10);
}
const arr = [];
for (let i = 0; i < 10; i++) {
arr.push(someFn()); // <-- () added
}
await Promise.all(arr);
console.log("item 1", items);
})();
you’re pushing someFn but you want someFn(). note we’re calling the function.

How can I (efficiently) chain my promise that returns an array with value rather than promiess?

I have a promise chain that finds an array. I want the array to return some results (where in the future I can set it to a state variable). However, I am only getting promises. I am wondering what I am doing wrong here.
For now, I am console logging the items in my array to see if it returns the value, rather than the Promise.
addIPFSItem = () => {
var finalItems = []
var searchAddress = "0x9Cf0dc46F259542A966032c01DD30B8D1c310e05";
const contract = require('truffle-contract')
const simpleStorage = contract(SimpleStorageContract)
simpleStorage.setProvider(this.state.web3.currentProvider)
this.state.web3.eth.getAccounts((error, accounts) => {
simpleStorage.deployed().then((instance) => {
this.simpleStorageInstance = instance
return this.simpleStorageInstance.getLength(searchAddress);
}).then((accountLength) => {
var movieItems = []
var i;
var indexCounter = 0;
//WITHIN THIS LOOP extend the chain to add to movies
for (i = 0; i < accountLength; i++) {
var p = this.simpleStorageInstance.getBook(searchAddress, i, { from: searchAddress }).then((hashVal) => {
return hashVal;
}).then((hashVal)=>{
var ipfsPrefix = "https://ipfs.io/ipfs/";
var ipfsURL = ipfsPrefix + hashVal;
return ipfsURL
})
var k = this.simpleStorageInstance.getTitle(searchAddress, i, { from: searchAddress }).then((title) => {
return title;
})
var l = this.simpleStorageInstance.getContents(searchAddress, i, { from: searchAddress }).then((contents) => {
return contents;
})
movieItems.push({id: i, poster_src: p, title: k, overview: l})
indexCounter++
console.log('counter: ', i, p, k, l)
}
//return items
return movieItems
}).then((array) =>{
var i
for(i=0; i<array.length; i++){
console.log('Array item: ', array[i])
}
return finalItems
})
})
}
I don't know if I need to chain the var p, var k, var l when adding it to movieItems array. Supposedly, after I push to movieItems, I can then try and get each value within the array as I have already retrieved the values right?
You are trying to cheat the promises here!
When you run
var l = this.simpleStorageInstance.getContents(searchAddress, i, { from: searchAddress }).then((contents) => {
console.log('Retrieved contents');
return contents;
});
movieItems.push({id: i, poster_src: p, title: k, overview: l});
console.log('Pushed!')
the javascript engine is going say "I'll put this Promise into l directly and continue to movieItems.push". If you run this, you'll see the logs appear as
Pushed!
Retrieved contents
There is a lot to learn about promises, so I can't explain everything in this one post. A quick fix for now, assuming you have an environment or build tool that supports it, is using async/await. That way you can make javascript "just wait" until your promises are resolved.
first you change
}).then((accountLength) => { to }).then(async (accountLength) => {
which tells javascript you are going to use await and work with Promises inside the function. Then you can, instead of using .then, use await like this:
// Here I use `await` instead of `.then` to make the promise return "normally"
var hashVal = await this.simpleStorageInstance.getBook(searchAddress, i, { from: searchAddress });
// I do the transformations you did inside `.then(() => { ... })` "just" after the function call
var ipfsPrefix = "https://ipfs.io/ipfs/";
var ipfsURL = ipfsPrefix + hashVal;
var p = ipfsURL;
// Again, using `await` instead of `.then`
var k = await this.simpleStorageInstance.getTitle(searchAddress, i, { from: searchAddress })
var l = await this.simpleStorageInstance.getContents(searchAddress, i, { from: searchAddress });
This would make your function actually return an array of values instead of an array with promises inside.
Hope this helps! :)
That's because the value of variables p, k, and l are Promises. And they will always be promises, because that's what this.simpleStorageInstance.getBook() et al return. Adding a .then() to them doesn't make them synchronously represent their values, they are still Promises. You need to compose Promises cleverly in order to ensure the values you want are available at the time and place you want them - I suggest you do more reading on how they work.
A straightforward fix might be to simply use await. It basically pauses execution until the asynchronous Promise finishes, and will ensure that p, k, and l represent the values that you expect of them. MDN documentation.
addIPFSItem = () => {
var finalItems = []
var searchAddress = "0x9Cf0dc46F259542A966032c01DD30B8D1c310e05";
const contract = require('truffle-contract')
const simpleStorage = contract(SimpleStorageContract)
simpleStorage.setProvider(this.state.web3.currentProvider)
this.state.web3.eth.getAccounts((error, accounts) => {
simpleStorage.deployed().then((instance) => {
this.simpleStorageInstance = instance
return this.simpleStorageInstance.getLength(searchAddress);
}).then((accountLength) => {
var movieItems = []
var i;
var indexCounter = 0;
//WITHIN THIS LOOP extend the chain to add to movies
for (i = 0; i < accountLength; i++) {
let p = await this.simpleStorageInstance.getBook(searchAddress, i, { from: searchAddress }).then((hashVal) => {
return hashVal;
}).then((hashVal) => {
var ipfsPrefix = "https://ipfs.io/ipfs/";
var ipfsURL = ipfsPrefix + hashVal;
return ipfsURL
});
let k = await this.simpleStorageInstance.getTitle(searchAddress, i, { from: searchAddress });
let l = await this.simpleStorageInstance.getContents(searchAddress, i, { from: searchAddress });
movieItems.push({ id: i, poster_src: p, title: k, overview: l })
indexCounter++
console.log('counter: ', i, p, k, l)
}
//return items
return movieItems
}).then((array) => {
var i
for (i = 0; i < array.length; i++) {
console.log('Array item: ', array[i])
}
return finalItems
})
})
}
P.S. If you're using const you should be using let instead of var. There is basically no reason whatsoever to use var in ES6.

Resolving Promise.all with a promise as part of array or object

I am trying to queue a bunch of asynchronous calls to fire in parallel. However, the promises I am queuing have additional data that I want to keep along with the promise value.
My question is: how can I pass an object or array, which contains a promise, and have the promises resolve to a value within the object?
For example, let’s generate a normal array of promises:
async function asyncFunction(input) {
return input * 10;
}
async function main() {
var promises = [];
for (let i = 0; i < 10; i++) {
promises.push(asyncFunction(i));
}
var result = await Promise.all(promises);
document.getElementById('output').innerText = JSON.stringify(result);
}
main();
<div id='output'></div>
This is working just fine. But now if we try to place the promise into an object, with some metadata:
async function asyncFunction(input) {
return input * 10;
}
async function main() {
var promises = [];
for (let i = 0; i < 10; i++) {
promises.push({
id: i,
value: asyncFunction(i)
});
}
var result = await Promise.all(promises);
document.getElementById('output').innerText = JSON.stringify(result);
}
main();
<div id='output'></div>
The value in value is a promise, and not the resolved value.
My expected output is:
[{"id":0,"value":0},{"id":1,"value":10},{"id":2,"value":20},...]
You can push promises that resolve to the format you want:
async function asyncFunction(input) {
return input * 10;
}
async function main() {
let promises = [];
for (let i = 0; i < 10; i++) {
promises.push(
asyncFunction(i).then(value => ({
id: i,
value,
}))
);
}
let result = await Promise.all(promises);
console.log(result);
}
main();
You could map over the array of promises and await the value key containing the promise to resolve
async function asyncFunction(input) {
return input * 10;
}
async function main() {
var promises = [];
for (let i = 0; i < 10; i++) {
promises.push({
id: i,
value: asyncFunction(i)
});
}
var result = await Promise.all(promises.map(async (promise) => ({
id: promise.id,
value: await promise.value
})));
document.getElementById('output').innerText = JSON.stringify(result);
}
main();

Categories

Resources