Reputation: 138
Got to love nodeJS and asynchronous nature! With that, I'm dumbfounded how to to continue bc I can't keep nesting promises and of course that's a not go so I'm throwing up my hands bc each step requires a completed action with data from the previous step.
This is what I'm trying to accomplish and code is below.
exports.newSessionNotifer = functions.database.ref('/sessions/college').onCreate((snap, context) => {
const college = snap.val();
var promises = [];
var getAdvisors = admin.database().ref('colleges').child(college).once('value').then((snapshot) => {
const people = snapshot.val();
var advisors = Object.keys(people);
return advisors;
}).then((advisors) => {
return advisors.forEach((token) => {
var advisorToken = admin.database().ref('users').child(token).child('fcmtoken').child('token').once('value');
return console.log(advisorToken);
});
});
return Promise.all(promises).then((values) => {
console.log(promises);
return console.log('Hi');
});
Upvotes: 0
Views: 871
Reputation: 62686
You're on the right track. once()
returns a promise, and it is the set of promises from repeated calls to once that must be collected and run with Promise.all()
.
exports.newSessionNotifer = functions.database.ref('/sessions/college').onCreate((snap, context) => {
const college = snap.val();
return admin.database().ref('colleges').child(college).once('value');
}).then(snapshot => {
const people = snapshot.val();
let advisors = Object.keys(people);
let promises = advisors.map(token => {
return admin.database().ref('users').child(token).child('fcmtoken').child('token').once('value');
});
return Promise.all(promises);
});
EDIT Editing again, this time with the OP's answer in hand. On style, I'm not sure what lint says, but my definition of bad nesting style is when a then()
block contains another then()
block. Also regarding style, my approach to making this stuff comprehensible is to build (and test) small functions, one per async task.
On structure, the OP's new answer unnecessarily chains a second block after return advisors
. Since advisors isn't a promise, we can carry on from there with synchronous code. Also on structure, the OP's solution creates a series of promises -- two for each advisor (get advisor token and push) -- but these are not certain to complete unless Promise.all is applied and returned.
Summing all that, my advice would be as follows...
On create, get the advisors for the college, send each a message.
exports.newSessionNotifer = functions.database.ref('/sessions/{sessionID}/college').onCreate((snap, context) => {
const college = snap.val();
return advisorsForCollege(college).then(advisors => {
let promises = advisors.map(advisor => sendAdvisorMessage(advisor, college));
return Promise.all(promises);
});
});
Advisors for a college are apparently the keys from that college object
function advisorsForCollege(college) {
return admin.database().ref('colleges').child(college).once('value').then(snapshot => Object.keys(snapshot.val()));
}
Sending an advisor message means getting the advisors token and doing a push. Return a two-promise chain that does that...
function sendAdvisorMessage(advisor, college) {
return tokenForAdvisor(advisor).then(token => {
let title = `There's a new session for ${college}!`;
let body = 'Go to the middle tab and swipe right to accept the session before your peers do!'
return sendToDevice(token, title, body);
});
}
Now we just need one to get an advisor's token and one to do a push...
function tokenForAdvisor(advisor) {
return admin.database().ref('users').child(advisor).child('fcmtoken').child('token').once('value');
}
function sendToDevice(token, title, body) {
const payload = { notification: { title: title, body: body } };
return admin.messaging().sendToDevice(token, payload);
};
I think lint should report all of the foregoing as just fine, even with promise nesting warning turned on.
Upvotes: 2
Reputation: 138
Thanks to danh, here's my final code. Comment/feedback away! I decided to disable the promise nesting option within lint and viola!
exports.newSessionNotifer = functions.database.ref('/sessions/{sessionID}/college').onCreate((snap, context) => {
const college = snap.val();
return admin.database().ref('colleges').child(college).once('value').then((snapshot) => {
const people = snapshot.val();
let advisors = Object.keys(people);
return advisors;
}).then((advisors) => {
return advisors.map(advisor => {
return admin.database().ref('users').child(advisor).child('fcmtoken').child('token').once('value').then((snapshot) => {
const token = snapshot.val();
const payload = {
notification: {
title: `There's a new session for ${college}!`,
body: 'Go to the middle tab and swipe right to accept the session before your peers do!'
}
};
return admin.messaging().sendToDevice(token, payload);
});
});
});
});
Upvotes: 2