Javascript asynchronous behavior for loops - javascript

I was fetching data asynchronously using async/await and then I am running two for loops one after another. So my question is will the for loops overlap each other for big data sets as js is asynchronous and if yes how to solve this?
And for what condition loops can overlap?
Actually, I am trying to make a dropdown and its working but I had this doubt.
const createDropdown = async language => {
let i = 0;
let listPromise = new Promise((resolve, reject) => {
db.ref("Products/" + language).on('value', snapshot => {
resolve(snapshot.val())
})//Fetching Data
})
let list = await listPromise;
for(i in list)
dropdown.remove(0)
for(i in list)
dropdown.options[dropdown.options.length] = new Option(list[i].name, list[i].name)
}
I am running this code and the for loops are not getting overlapped but is there a condition that it will?

Loops which are put in the code one after the other will never overlap either the code inside the loops are synchronous or asynchronous.
for (var i = 0; i < 10; i++) {
doSomethingSync()
}
for (var j = 0; j < 10; j++) {
createPromise().then((res) => {console.log(res))
}
for (var k = 0; k < 10; k++) {
var res = await createPromise();
console.log(res);
}
Above, the "I" loop completes all its operations and then the "J" loop, and then the "K" loop.
Here is the order and the details of each operation
The "I" loop serializes the 10 synchronous operations.
The "J" loop create 10 different promises. They maybe resolved in a different order.
The "K" loop creates 10 serialized promises. Each iteration waits until the promise is resolved before going for the net one.
1, 2, and 3 always happen one after the other.

I used this solution to work with axios, with about 1000 requests to a server and works fine, maybe this can be your solution too. In this code you will make a promise of your db.ref and wait for the response then you use that to manipulate.
const createDropdown = async (language) => {
const listPromise = await Promise.all(
return await db.ref("Products/" + language).on ('value', snapshot => snapshot.val())
)
for(i in listPromise)
dropdown.remove(0)
for(i in listPromise)
dropdown.options[dropdown.options.length] = new Option(list[i].name, list[i].name)
}

Related

function does not return value React

I have a function where I form the current sum of elements. Getcoin is the method in my Api, and it works correctly if I console.log each element during the foreach cycle. But as the result I got nothing in the return and console.log function
getCurrentCost() {
let currentCostArr = [];
let sum = 0;
let existingEntries = JSON.parse(localStorage.getItem("walletData"));
if (existingEntries == null) existingEntries = [];
existingEntries.forEach(el => {
this.coincapService
.getCoin(el.id)
.then((element) => {
currentCostArr.push(element.priceUsd * el.amount)
})
});
for (let i = 0; i < currentCostArr.length; i++) {
sum += +currentCostArr[i];
}
console.log('cevf c ' + sum)
console.log('current cost ' + currentCostArr)
return currentCostArr;
}
getCoin method
getCoin = async (id) => {
const coin = await this.getResource(`/assets/${id}`);
return this._transformCoin(coin);
}
The problem is that you are not handling correctly the Promise that .getCoin() returns inside the .forEach() 'loop'. It's making multiple calls to getCoin but the code outside the forEach is not awaiting these multiple async calls, so the next lines will execute without awaiting the result of the Promises. This is causing confusion in execution order logic of your code, behaving different than what you are expecting. Actually the function getCurrentCost() is ending before the Promises inside the loop resolves.
One way to solve your problem is replacing the .forEach() with .map(), which will return several Promises that will be awaited will Promise.all(), behaving the way it's expected.
//don't forget to turn the function async, so you can use await keyword.
async getCurrentCost() {
let sum = 0;
let existingEntries = JSON.parse(localStorage.getItem("walletData"));
if (existingEntries == null) existingEntries = [];
await Promise.all(existingEntries.map(async (el) => {
const coin = await this.coincapService.getCoin(el.id)
currentCostArr.push(coin.priceUsd * coin.amount)
})
return currentCostArr
}
Your forEach loop pushes values in you array asynchronously.
You calculate the sum before you get your array filled up with values.

How do you making javascript blocking code? [duplicate]

let currentProduct;
for (let i = 0; i < products.length; i++) {
currentProduct = products[i];
subscription.getAll(products[i]._id)
.then((subs) => {
update(subs, currentProduct);
});
}
I'm using bluebird, the methods getAll and update return promises. How can I say "Wait until the two promises return, then update the currentProduct value"? I'm quite new to JS...
This will be straightforward if you can use async/await:
// Make sure that this code is inside a function declared using
// the `async` keyword.
let currentProduct;
for (let i = 0; i < products.length; i++) {
currentProduct = products[i];
// By using await, the code will halt here until
// the promise resolves, then it will go to the
// next iteration...
await subscription.getAll(products[i]._id)
.then((subs) => {
// Make sure to return your promise here...
return update(subs, currentProduct);
});
// You could also avoid the .then by using two awaits:
/*
const subs = await subscription.getAll(products[i]._id);
await update(subs, currentProduct);
*/
}
Or if you can only use plain promises, you can loop through all your products, and put each promise in the .then of the last loop. In that way, it will only advance to the next when the previous has resolved (even though it will have iterated the whole loop first):
let currentProduct;
let promiseChain = Promise.resolve();
for (let i = 0; i < products.length; i++) {
currentProduct = products[i];
// Note that there is a scoping issue here, since
// none of the .then code runs till the loop completes,
// you need to pass the current value of `currentProduct`
// into the chain manually, to avoid having its value
// changed before the .then code accesses it.
const makeNextPromise = (currentProduct) => () => {
// Make sure to return your promise here.
return subscription.getAll(products[i]._id)
.then((subs) => {
// Make sure to return your promise here.
return update(subs, currentProduct);
});
}
// Note that we pass the value of `currentProduct` into the
// function to avoid it changing as the loop iterates.
promiseChain = promiseChain.then(makeNextPromise(currentProduct))
}
In the second snippet, the loop just sets up the entire chain, but doesn't execute the code inside the .then immediately. Your getAll functions won't run until each prior one has resolved in turn (which is what you want).
Here is how I'd do it:
for (let product of products) {
let subs = await subscription.getAll(product._id);
await update(subs, product);
}
No need to manually chain promises or iterate arrays by index :)
You may want to keep track of what products you've processed because when one fails you have no idea how many succeeded and you don't know what to correct (if roll back) or retry.
The async "loop" could be a recursive function:
const updateProducts = /* add async */async (products,processed=[]) => {
try{
if(products.length===0){
return processed;
}
const subs = await subscription.getAll(products[0]._id)
await update(subs, product);
processed.push(product[0]._id);
}catch(err){
throw [err,processed];
}
return await updateProducts(products.slice(1),processed);
}
Without async you can use recursion or reduce:
//using reduce
const updateProducts = (products) => {
//keep track of processed id's
const processed = [];
return products.reduce(
(acc,product)=>
acc
.then(_=>subscription.getAll(product._id))
.then(subs=>update(subs, product))
//add product id to processed product ids
.then(_=>processed.push(product._id)),
Promise.resolve()
)
//resolve with processed product id's
.then(_=>processed)
//when rejecting include the processed items
.catch(err=>Promise.reject([err,processed]));
}
//using recursion
const updateProducts = (products,processed=[]) =>
(products.length!==0)
? subscription.getAll(products[0]._id)
.then(subs=>update(subs, product))
//add product id to processed
.then(_=>processed.push(products[0]._id))
//reject with error and id's of processed products
.catch(err=>Promise.reject([err,processed]))
.then(_=>updateProducts(products.slice(1),processed))
: processed//resolve with array of processed product ids
Here is how you'd call updateProducts:
updateProducts(products)
.then(processed=>console.log("Following products are updated.",processed))
.catch(([err,processed])=>
console.error(
"something went wrong:",err,
"following were processed until something went wrong:",
processed
)
)

Javascript array length always is 0

I tried map elements to [] for angular. But if I checked length of objects there is always 0..
var objects = [];
this.get().subscribe(
response => {
for (var i = 0; i < response.length; i++) {
objects.push(response[i]);
}
}
);
console.log(objects.length);
screen if I console.log(objects)
What I am doing wrong?
You are doing console.log before getting response in subscribe. Just move console.log inside subscribe.
var objects = [];
this.get().subscribe(
response => {
for (var i = 0; i < response.length; i++) {
objects.push(response[i]);
}
console.log(objects.length);
}
);
This is due to the asynchronous nature of execution of the code. The subscribe method accepts a callback, which is the arrow function that you have written with the parameter named response.
So, the callback will not be executed immediately but will be done after a while. But, since JS is asynchronus, it wouldn't wait for the callback to get executed and will move on to the next line of the code where the variable will still be an empty array.
As suggested in the other answers, you could put the console log within the callback function to log the expected value.
You are logging something that's set later so the preview shows an empty array but you can expand it.
The log will show the item as it is right now; not as it is when you log it.
var arr = [];
console.log(arr);//shows [] but when you expand it it shows items
arr.push({name:'john'});
If you want to see an object's values at the time you are logging the object and plan to mutate it after the log then you can do the following:
console.log(JSON.stringify(object,undefined,2);
Your objects.push(response[i]); statement is in a callback and that callback is probably called after your console.log(objects.length); you can proof that by adding another log:
var objects = [];
this.get().subscribe(
response => {
console.log('second log')
for (var i = 0; i < response.length; i++) {
objects.push(response[i]);
}
}
);
console.log("first log",objects.length);
If you have a value that resolves once at a later time then you could use Promises but it looks like you subscribe to an event that can resolve multiple times so your response will only be available in the callback.
Seems that subscribe code is async, so you can wrap this up on a promise. result is the objects you want
const promise = new Promise((resolve, reject) => {
var objects = [];
this.get().subscribe(response => {
for (var i = 0; i < response.length; i++) {
objects.push(response[i]);
}
resolve(objects);
});
});
promise.then(result => console.log(result));

Await promises in async.eachLimit();

I have this JavaScript pseudocode:
async function main() {
for(let i=0; i<array.length; i++) {
for(let j=0; j<3; j++) {
let value = await promise thisFunctionReturnsAPromise();
If(value) {
// do_something
}
}
}
}
Now I need to iterate 2 promises at time (to speed up the job) and to test each one of them 3 times; so i can't use a simple for anymore.
I need to replace the external for loop.
I was thinking to use async.eachLimit to solve the problem but i can't use await within eachLimit. How can i put an await within async eachLimit to call more then one "test cicle"?
I need something like async.eachLimit because my job needs to go to the next element that needs to be tested after one of the previous two has been tested for a maximum of 3 times.
Sorry for my bad english.

Can't stop running promises

I have a complicated javascript function with promises inside it.
Here is my code :
var chunkProjectList = function(list, project, accounts) {
return new Promise((resolve, reject) => {
// init delta
let delta = 5
if ((accounts.length * delta) > list.length ) {
delta = (list.length / accounts.length)
}
// init chunked list
let chunkedList = []
for (let i = 0; i < accounts.length; i++) {
chunkedList.push([])
}
// loop on all users
for (let i = 0; i < list.length; i++) {
isInBlacklist(list[i], project.id)
.then(() => { // current user is in the blacklist
ProjectModel.deleteElementFromList(project.id, list[i])
if (i === list.length - 1) {
// no screen_names available, cu lan
return resolve(chunkedList)
}
})
.catch(() => { // we continue
let promises = []
for (let j = 0; j < accounts.length; j++) {
promises[j] = FollowedUserModel.getFollowedUserByScreenName(list[i], accounts[j].id)
}
// we checked a screen_name for all accounts
Promise.all(promises)
.then((rows) => {
for (let k = 0; k < rows.length; k++) {
if (rows[k][0].length === 0 && chunkedList[k].length < delta && list[i] !== '') {
console.log('we push')
chunkedList[k].push(list[i])
break
}
console.log('we cut (already followed or filled chunklist)')
if (k === rows.length - 1) {
// determine if is cancer screen_name or just filled chunklists
for (let l = 0; l < chunkedList.length; l++) {
if (chunkedList[l].length < delta) {
console.log('we cut because nobody wants ya')
ProjectModel.deleteElementFromList(project.id, list[i])
ProjectModel.addElementToBlackList(project.id, list[i])
}
if (l === chunkedList.length - 1) {
// you are all filled, cu lan
return resolve(chunkedList)
break
}
}
}
}
if (i === list.length - 1) {
// no screen_names available, cu lan
over = 1
return resolve(chunkedList)
}
})
})
}
})
}
My program is looping on a list of usernames, and tries to share it between the accounts, with a limit called 'delta' for each account
example:
My list contains 1000 usernames, the delta is 100 and I have 4 accounts
The expected result is an array of arrays, containing for each arrays, 100 usernames.
chunkedList (array) => ( array 1: length 100, array 2: length 100 ... )
The problem I have is the following :
When the delta is reached for all the chunkedList arrays, I just wanna exit this function, and stop every work inside, even the running promises. Just when I 'return resolve(chunkedList)'.
But the program continues to run, and it's a real problem for performances.
I know it's difficult to understand the code. Thanks
I am having difficulty understanding what your code is trying to accomplish. However, it looks like you could split your code up into a few separate promise functions. This would help clarity and should allow you stop execution. Without being able to test your code, the main problem seems to be caused by your main loop. In your code you have the following:
.then(() => {
....
return resolve(chunkedList);
}
The "return" statement here is only going to return from the inner .then() call and not from your main Promise. Thus, your main loop will continue to execute all of its code on the whole array. Essentially, it seems that your loop will continue to modify your main Promise's resolved value and never return until the whole loop has completed.
My recommendation is that you split out your main loop contents to be a separate method that takes a list as a parameter. This method will return a Promise. With that method, you can create a Promise.all() using your main loop and then add a .catch() method. The catch() method will occur when you reject one of the method calls (therefore not completing the rest of the promises in the rest of the Promise.all array).
I apologize if my above suggestion does not make any sense. I am trying to write this quickly. In summary, if you can split out the different steps of your method into their own methods that return their own promises, you can use promises very effectively allowing you to chain, execute multiple tasks in parallel, and/or execute tasks sequentially.

Categories