Wait end of two subscribe to make an operation - javascript

I have two subscribe like this :
this.birthdays = await this.birthdaySP.getBirthdays();
this.birthdays.subscribe(groups => {
const allBirthdayT = [];
groups.map(c => {
allBirthdayT.push({
key: c.payload.key,
...c.payload.val()
})
})
console.log(allBirthdayT);
});
this.birthdaysInGroups = await this.birthdaySP.getBirthdaysInGroups();
this.birthdaysInGroups.subscribe(groups => {
const allBirthdayB = [];
groups.map(c => {
c.birthdays.subscribe(d => {
d.map(e => {
allBirthdayB.push(e);
})
})
})
console.log(allBirthdayB);
});
I would like to wait the end of this two subscribes to compare allBirthdayB and allBirthdayT arrays (i receive datas in two console.log).
this.birthdaySP.getBirthdays() and this.birthdaySP.getBirthdaysInGroups() are two observable that receive data from firebase.
The first Observable is like that :
async getBirthdays() {
const user = await this.authSP.getUserInfo();
return this.angularFire.list('birthdays', ref => ref.orderByChild('creator_user_id').equalTo(user.uid)).snapshotChanges();
}
I try with forkJoin but i don't know how i can use it to solve this problem
Any tips?

You can use the combineLatest() function.
Example:
combineLatest(observable1$, observable2$)
.subscribe(([observable1, observable2]) => {
console.log(observable1, observable2);
});

Related

Trying to pass array and use foreach to send back multiple data

I had getProductInfo orgianlly, as two parameters, where it would be (res, sku). but now I want to pass a set object with sku numbers and for-each res.send the data
const activeProductBank = new Set([6401728, 6430161, 6359222, 6368084]);
getProductInfo = (res) => {
activeProductBank.forEach((SKU) => {
bby.products(SKU, { show:'sku,name' })
.then(function(data) {
res.send(data);
});
})
};
also tried this
getProductInfo = (res) => {
const allProductInfo = '';
activeProductBank.forEach((SKU) => {
bby.products(SKU, { show:'sku,name'})
.then(function(data) {
allProductInfo.concat(data);
});
})
res.send(allProductInfo);
};
The error I get "app listening at http://localhost:3000
(node:25556) UnhandledPromiseRejectionWarning: Error: Exceeded max retries"
You can use a combination of ASYNC / AWAIT and Promise.all to populate the allProductInfo as expected.
The caveat with ASYNC / AWAIT is that you can only use ASYNC function inside an ASYNC function. More about it here https://javascript.info/async-await
activeProductBank.map will iterate over all your activeProductBank and returns an array of Promises which are then passed over to the Promise.all which then resolves after all the promises in the list are reolved.
Promise.all
getProductInfo = async (res) => {
const allProductInfo = Promise.all(
activeProductBank.map(SKU => bby.products(SKU, { show:'sku,name'}))
)
res.send(allProductInfo);
};
Another approach is to use for..of loop and pushing the response of each productInfo one by one using the Await call like below
getProductInfo = async (res) => {
let allProductInfo = [];
for(let sku of allProductInfo) {
const productInfo = await bby.products(sku, { show:'sku,name'});
allProductInfo.push(productInfo);
}
res.send(allProductInfo);
};

Async request to firebase inside forEach

I have an array like that (generatedArray):
[
{
...some data,
[deals],
type: 'uo'
},
{
...some data,
[deals],
type: 'pif',
pifKua: 'some value'
},
...other similar objects
]
If type is 'pif' I want to take snapshot from firebase and put snapshot's value in pifKua field. To do it I have a such function:
handleCreateDocs = () => {
this.generatedArray.forEach( item => {
console.log('itemtype', item.type);
if (item.type === 'pif') {
const stocksRef = firebase.database().ref('clients');
const kua = stocksRef.orderByChild('shortName').equalTo(item.pifKua);
kua.once('value').then( snapshot => {
snapshot.forEach((child) => {
Object.assign(item.pifKua, child.val());
});
})
}
});
this.setState({
kuaCheck: true
}, () => {console.log('kua', this.generatedArray)});
};
But after this function this.generatedArray don't changes. In console i can see 'kua', but this.generatedArray still the same witjout any changes. Help me to fix it please.
You should only call setState until you have all the data available. Right now, you are calling it without waiting for any of the queries to complete. then does not cause your code to pause and wait for the result. It just returns another promise that resolves some time later while your code continues in the forEach loop.
You will need to collect your promises into an an array, and use Promise.all() to wait for them to complete. The general form of your code will be more like this:
handleCreateDocs = () => {
const promises = [];
this.generatedArray.forEach( item => {
console.log('itemtype', item.type);
if (item.type === 'pif') {
const stocksRef = firebase.database().ref('clients');
const kua = stocksRef.orderByChild('shortName').equalTo(item.pifKua);
const promise = kua.once('value').then( snapshot => {
snapshot.forEach((child) => {
Object.assign(item.pifKua, child.val());
});
});
promises.push(promise);
}
});
Promise.all(promises).then(() => {
console.log("all queries complete");
this.setState({
kuaCheck: true
}, () => {console.log('kua', this.generatedArray)});
});
};

How to use foreach and promise

I need to get datas with nested foreach, but I can't fill my array.
At the end of this code I would like to have an array (segId) with my datas but it is empty (because of aynschronous).
I read that I had to use Promise.all but I can't beacause my promise are nested
I'm beginner so my code is far from perfect
How can I do that ?
async function getActivities(strava, accessToken)
{
const payload = await strava.athlete.listActivities({'access_token':accessToken, 'after':'1595281514', 'per_page':'10'})
return payload;
}
async function getActivity(strava, accessToken, id)
{
const payload = await strava.activities.get({'access_token':accessToken, 'id':id, 'include_all_efforts':'true'})
return payload;
}
async function getSegment(strava, accessToken, id)
{
const payload = await strava.segments.get({'access_token':accessToken,'id':id})
return payload
}
var tableau = []
var segId = []
const activities = getActivities(strava, accessToken)
activities.then(value => {
value.forEach((element, index) => {
const activity = getActivity(strava, accessToken, element['id'])
activity.then(value => {
value['segment_efforts'].forEach((element, index) => {
const segment = getSegment(strava, accessToken, element['segment']['id'])
segment.then(value => {
segId.push(value['id'])
})
//console.log(segId)
});
});
})
}) console.log(segId)
Regards
PS : Sorry for my english ...
Something like this should work. You need to always return the inner promises to include them in your promise chain. Consider splitting the code into functions to make it more readable.
getActivities(strava, accessToken).then(activities => {
return Promise.all(activities.map(elem => {
return getActivity(strava, accessToken, elem['id']).then(activity => {
return Promise.all(activity['segment_efforts'].map(elem => {
return getSegment(strava, accessToken, elem['segment']['id']).then(segment => {
segId.push(segment['id']);
});
}));
})
}));
})
.then(_ => {
console.log(segId);
});

React & Firebase - making multiple Firebase calls and waiting for all promises to be complete

I would like to change the following code as it reaches Firebases 10 item in an array limit. I want to loop through all teamIds and make a Firebase query for each individual teamId. The issue is, I'm not sure how to do this in a way that it waits until all promises are complete before continuing.
This is the current code;
const unsubscribe = Firebase.firestore().collection('invites').where('teamId', 'in', teamIds).onSnapshot(snapshot => {
const invites = [];
snapshot.docs.forEach(doc => {
const data = doc.data();
if (!invites[data.teamId]) {
invites[data.teamId] = [];
}
invites[data.teamId].push(Object.assign({}, { id: doc.id }, doc.data()));
});
setTeamInvites(invites);
setLoading(false);
setError(false);
});
I've like to change it to something like this;
teamIds.forEach(teamId => {
Firebase.firestore().collection('invites').where('teamId', '==', teamId).onSnapshot(snapshot => {
// Map the results to an array that will be stored in the pageState when all promises are complete
});
});
How can I do this?
I figured out I can do this with Promise.all, this is what I ended up with.
const promises = [];
teamIds.forEach(teamId => {
promises.push(new Promise((resolve, reject) => {
Firebase.firestore().collection('invites').where('teamId', '==', teamId).onSnapshot(snapshot => {
let invites = [];
invites[teamId] = [];
snapshot.docs.forEach(doc => {
const data = doc.data();
invites[data.teamId].push(Object.assign({}, { id: doc.id }, data));
resolve(invites);
});
});
}));
});
Promise.all(promises).then(allInvites => {
// Do what I needed to do with all of the invites here
});

ReactJS how to wait for all API calls to be ended in componentDidMount of simple component

I'm using latest react and very basic app which calls 3rd party service API which actually is not well designed in meaning of following.
I have to execute one call which return list and then have to iterate and call other end point to get data for item from list and then again in data have new list for which I have to call 3rd API end point.
After I receive all data I combined it to one items array and place it in state in componentDidMount function but this final step works only if I surround it with setTimeout.
Is there some elegant way to do that?
I'm using fetch and really pure react components, have my own simple service from where I call API, here is some code parts...
items[tag].sensors = [];
API.getObjects(sessionData, userDetails, tag).then(links => {
Object.keys(links.link).forEach(link => {
API.getObjects(sessionData, userDetails, link).then(objLink => {
Object.keys(objLink.link).forEach(function (key) {
let obj = objLink.link[key];
if (obj && obj.type === 'sensor') {
API.getSensorNames(sessionData, key).then(response => {
const sensor = response.sensor;
// some sensor calculations....
items[tag].sensors.push(sensor);
});
}
});
});
});
});
// this part only works if it's surrounded with timeout
setTimeout(function() {
let processedItems = [];
for (var key in items) {
if (items.hasOwnProperty(key)) {
processedItems.push(items[key]);
}
}
self.setState({
items: processedItems
});
}, 1000);
Thanks in advance.
Simply, You can use Promise to wait until you get values from the API call, therefore you will put your code in function like this
function prepareItems() {
items[tag].sensors = [];
return new Promise((resolve, reject) => {
API.getObjects(sessionData, userDetails, tag).then(links => {
Object.keys(links.link).forEach(link => {
API.getObjects(sessionData, userDetails, link).then(objLink => {
Object.keys(objLink.link).forEach(function(key) {
let obj = objLink.link[key];
if (obj && obj.type === "sensor") {
API.getSensorNames(sessionData, key).then(response => {
const sensor = response.sensor;
// some sensor calculations....
items[tag].sensors.push(sensor);
// whenever you set resolve it will end the promise
//and pass the result it to the then function
resolve(items)
});
}
});
});
});
});
});
}
and use then to get the result from the prepareItems function after its resolved
prepareItems().then(items => {
//Do what ever you want with prepared item
})
What about using async/await operators.
These operators allows you to wait until the response is ready.
You can use this kind of helper function.
getItems = async (...) => {
...
items[tag].sensors = []
const links = await API.getObjects(sessionData, userDetails, tag)
Object.keys(links.link).forEach(async (link) => {
const objLink = await API.getObjects(sessionData, userDetails, link)
Object.keys(objLink.link).forEach(async (key) => {
let obj = objLink.link[key]
if (obj && obj.type === 'sensor') {
const response = await API.getSensorNames(sessionData, key)
const sensor = response.sensor
items[tag].sensors.push(sensor)
}
})
})
this.setState({ items })
}
Also you can see this great documentation.

Categories