Using Promise.all() when iterating over a .JSON? - javascript

I have a .JSON with several different types of data structures which I iterate over with a group of for loops, each for loop has an async function running on it - namely, an insert on a SQLite database - but I want to wait until all of the inserts from all of the for loops have finished. This is one such loop:
for (let i = 0; i < data.json().products.length; i++) {
this.product.addItem(data.json().products[i]);
}
And this is the addItem function:
addItem(product){
this.productsSQL.query('INSERT INTO ...').then((data) => {
...
}, (error) => {
...
});
}
I would imagine I have to change addItem to be:
addItem(product){
return this.productsSQL.query('INSERT INTO ...').then((data) => {
return true;
}, (error) => {
return false;
});
}
And then each loop I need to use a promise.all() but then I also need a promise.all() that has all of the loops together?

Basically create a array of promises and update your code to the following should work:
var promises = [];
for (let i = 0; i < data.json().products.length; i++) {
promises.push(this.product.addItem(data.json().products[i]));
}
Promise.all(promises).then((data) => {
return true;
}, (error) => {
return false;
});
Have addItem return a promise:
addItem(product){
return this.productsSQL.query('INSERT INTO ...');
}

Related

Callback function inside for loop - Nodejs

Hope you're having a good day. I recently discovered that it is not as easy to handle callbacks inside a for loop. I have tried a few things but couldn't find a solution.
Here is the code:
var book = new Array;
var chapters = Object.keys(epub.spine.contents).length;
for (let i = 0; i < chapters; i++) {
let cacheArray = [];
epub.getChapter(epub.spine.contents[i].id, function (err, data) {
if (err) {
return console.log(err);
}
//remove html tags
let str = data.replace(/<\/?[\w\s]*>|<.+[\W]>/g, '');
book.push(str)
})
}
console.log(book)//returns empty array ofc
After this code is executed, I need to loop over the array to search its contents. If that was not the case, my approach would be to just send it to a db.
My approach was the following:
var recursiveChapter = function (n) {
var book = new Array;
if (n < chapters) {
// chapter function
epub.getChapter(epub.spine.contents[n].id, function (err, data) {
if (err) {
throw err
}
//remove HTML tags
let str = data.replace(/<\/?[\w\s]*>|<.+[\W]>/g, '');
book.push(str)
recursiveChapter(n + 1)
});
}
}
//start the recursive function
recursiveChapter(0)
console.log(book)//returns an empty array
I am stuck and can't think of a way of using this data without storing it in a db.
Any help would be appreciated .
There are a few ways you can tackle this. One way is to use the async library, this allows to to run async. calls in parallel, or in series.
For example, we can use async.mapSeries() to get the result of a series of asynchronous calls as an array.
You input your array of ids, then the callback will return an array of the data returned, for example, I've mocked out the getChapter() function to give you an idea of how it would work:
// Mock out epub object
const epub = {
getChapter(id, callback) {
setTimeout(() => callback(null, "Data for id " + id), 250);
}
}
let ids = [1,2,3,4];
console.log("Calling async.mapSeries for ids:", ids);
async.mapSeries(ids, epub.getChapter, (err, result) => {
if (err) {
console.error(err);
} else {
console.log("Result:", result)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/async/3.2.0/async.min.js" integrity="sha512-6K6+H87tLdCWvY5ml9ZQXLRlPlDEt8uXmtELhuJRgFyEDv6JvndWHg3jadJuBVGPEhhA2AAt+ROMC2V7EvTIWw==" crossorigin="anonymous"></script>
You could also promisify the epub call and use Promise.all to get the result, like so:
epub = {
getChapter(id, callback) {
setTimeout(() => callback(null, "Data for id " + id), 250);
}
}
let ids = [1,2,3,4];
function getChapterPromisified(id) {
return new Promise((resolve, reject) => {
epub.getChapter(id, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
})
})
}
// Create a list of promises, one for each call
const promises = ids.map(id => getChapterPromisified(id))
Promise.all(promises)
.then(result => console.log("Result:", result))
.catch(error => console.error("An error occurred:", err));

How to stop a js promise.all() on a condition?

I have this piece of code
let promiseList = []
for (let i in data) {
let promise = checkIfMember(data[i].tg_id, chatId).then(res => {
if (res) {
//if the user has a undefined username it won't be rained
if (data[i].username != "undefined") {
console.log(data[i].username)
members.push([data[i].tg_id, data[i].username])
}
}
}).catch(err => {
console.log(err)
})
promiseList.push(promise)
}
return Promise.all(promiseList).then((res) => {
//list of members that are in the room , we randomize it before returning
shuffleArray(members)
if(numberOfUsers > members.length){
return[false, members.length]
}else{
members = members.slice(0,numberOfUsers)
return [true, members]
}
});
Basically I have a promise list that is being filled. And then all of them are executed with a promiseAll. The problem is that I dont want all of them to execute, I want to to do something like this:
let promiseList = []
let validmembers = 0;
for (let i in data) {
let promise = checkIfMember(data[i].tg_id, chatId).then(res => {
if (res) {
//if the user has a undefined username it won't be rained
if (data[i].username != "undefined") {
console.log(data[i].username)
members.push([data[i].tg_id, data[i].username])
//stop there the execution
validmembers++;
if(validmembers == numberOfUsers){
return;
}
}
}
}).catch(err => {
console.log(err)
})
promiseList.push(promise)
}
return Promise.all(promiseList).then((res) => {
//list of members that are in the room , we randomize it before returning
shuffleArray(members)
if(numberOfUsers > members.length){
return[false, members.length]
}else{
members = members.slice(0,numberOfUsers)
return [true, members]
}
});
But the problem is that the promises are async, so the promiselist filling doesnt stop.
How would I solve this?
It appears you want the members array to have no more than numberOfUsers entries in it due to the promises. Since the promises run asynchronously you can't stop the checkIfMember function from being called, but inside of its then function you could avoid accumulating more members like this:
// don't use `!== "undefined"` unless it can actually be the string "undefined"
if (data[i].username) {
if (members.length < numberOfUsers) {
console.log(data[i].username);
members.push([data[i].tg_id, data[i].username]);
}
}
By only pushing to members when it's not yet full you will avoid adding more than numberOfUsers and don't need to do the slice later.
If instead you want to avoid the entire promise work when enough have been collected, then you can serialize them like this:
async function yourFunction() {
for (let i in data) {
if (members.length < numberOfUsers) {
// don't continue to the next 'i' until this one is done
await checkIfMember(data[i].tg_id, chatId)
.then(res => {
if (res) {
//if the user has a undefined username it won't be rained
if (data[i].username != "undefined") {
console.log(data[i].username);
members.push([data[i].tg_id, data[i].username]);
}
}
})
.catch(err => {
console.log(err);
});
} else {
break; // no need to continue once members is full
}
}
return [true, members];
}

how to validate all refs with vee-validate?

I have dynamic components that needs to be validated.
I have an array and i push my components there. The for loop works great.
validateForm() {
const PROMISES = [this.$refs.contactDetailsForm.$refs.contactDetails]
for (let i = 1; i <= this.count; i++) {
PROMISES.push(this.$refs[`passengerForm${i}`][0])
}
return Promise.all(PROMISES)
},
But problem is I do not know how to return the results of the validation. I want the results of this function in another function(Promise). how can I do that?
this is the solution:
validateForm() {
const PROMISES = [this.$refs.contactDetailsForm.$refs.contactDetails]
for (let i = 1; i <= this.count; i++) {
PROMISES.push(this.$refs[`passengerForm${i}`][0])
}
return new Promise((resolve, reject) => {
PROMISES.forEach(async (item) => {
const STATUS = await item.validate()
STATUS ? resolve(STATUS) : reject(STATUS)
})
})
}
Untested, but Promise.all returns an array of results for the promises. What you need to do is trigger validate for all the things you want to know the result for, collect those promises and then check the results in a Promise.all. You didn't give quite enough code to answer this fully but it's something like this:
validateForm() {
//both of these I added validate() to because I'm hoping they are references to ValidationObservers
const PROMISES = [this.$refs.contactDetailsForm.$refs.contactDetails.validate()]
for (let i = 1; i <= this.count; i++) {
PROMISES.push(this.$refs[`passengerForm${i}`][0].validate())
}
return Promise.all(Promises);
}
Then wherever you are calling this, you'd do:
this.validateForm().then((values) => {
this.formIsValid = values.every((result) => result));
//if the things above are ValidationProviders rather than VO, you have to use result.valid instead of result
});

Awaiting for a for loop to end to continue function in javascript

i have one question! I want the function getMealSummary() to return after the for loop has finished already finished (I didnt include the return key because the function is very big and i just had a doubt in this section).
How can I do it with async/await or promises?
Thanks
export const getMealMicroSummaryHelper = async function getMealSummary(array, populatedFoods, customMeasurements, isRecipe,expertId){
var [totalZinc, totalCalcium,totalAlphaCarotene,totalBetaCarotene,totalCholine,totalCopper,totalBetaCrypto,totalFluoride,totalVitaminB9,totalIron,
totalLutein,totalLycopene,totalMagnesium,totalManganese,totalNiacin,totalVitaminB5,totalPhosphorus,totalPotassium,totalRetinol,totalRiboflavin,
totalSelenium,totalSodium,totalTheobromine,totalVitaminA,totalVitaminB12,totalVitaminB6,totalVitaminC,totalVitaminD,totalVitaminD2,totalVitaminD3,
totalVitaminE,totalThiamin] = [0, 0, 0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
let recipesArray = []
for(let i = 0; i < populatedFoods.length; i++){
if(populatedFoods[i].foodType === 4){
await apiCall("get",`/api/experts/${expertId}/recipes/${populatedFoods[i]._id}`)
.then(recipe =>{
recipesArray.push(recipe)
})
.catch((err)=>console.log(err))
}
You can use a combination of map() and Promise.all for this like:
async function getMealSummary(array, populatedFoods, customMeasurements, isRecipe, expertId) {
try {
let recipesArray = await Promise.all(
populatedFoods.map(async populatedFoods => {
if (populatedFoods[i].foodType === 4) {
let recipe = await apiCall("get", `/api/experts/${expertId}/recipes/${populatedFoods[i]._id}`)
return recipe;
}
})
)
} catch (err) {
console.log(err)
}
}
This is just a simple version to give you a basic idea. I have not included all the variables that you have declared above recipesArray. Those will need to be included as needed.

Creating array in ES6 with async method calls

I have the following method that I'm trying to complete:
getAllValues: function (callback) {
this.getCount((count) => { // count is usually 5
let results = []
for (var i = 0; i < count; i++) {
this.getValue(i, (result) => { // getValue() is async and eventually returns a string
results.push(result)
})
if (i == count-1) {
callback(results)
}
}
I want results to be an array with all of the strings returned by getValue(); however, I haven't been able to figure out how to do this. In callback(results), results ends up being an empty array, so the pushed values are being dropped somehow
How do I get this to do what I want?
EDIT: I do NOT want to use promises here.
You're testing for the results completion in the wrong place
getAllValues: function(callback) {
this.getCount((count) => { // count is usually 5
let results = [];
let completed = 0;
for (let i = 0; i < count; i++) { // *** use let instead
this.getValue(i, (result) => { // getValue() is async and eventually returns a string
completed ++;
results[i] = result; // *** note this change to guarantee the order of results is preserved
if (completed == count) {
callback(results)
}
})
}
})
}
Note: use let in the for loop, so that i is correct inside
don't push ... assign to the index, to preserve order of results
and alternative, by having a "promisified" getValue (called it getValuePromise in the code below)
getValuePromise: function(i) {
return new Promise(resolve => {
this.getValue(i, resolve);
});
}
getAllValues: function(callback) {
this.getCount((count) =>
Promise.all(Array.from({length:count}).map((unused, i) => this.getValuePromise(i)))
.then(callback)
);
}
What you need to do is use then() it will wait for your async function to finish then it will run your callback.
getAllValues: function (callback) {
this.getCount((count) => { // count is usually 5
let results = []
for (var i = 0; i < count; i++) {
this.getValue(i, (result) => { // getValue() is async and eventually returns a string
results.push(result)
}).then(function(getValueResults){
if (i == count-1) {
callback(results)
}
})
}

Categories

Resources