how to handle array of 'promis' - node.js

I built a database that contains a list of users who receive messages from firebase with a request key. Every time a new request with status 'open' inserted, I am trying to sort them all by the value of 'Timestamp' and send it by this order to the receivers(each receiver will get one message).
if the list of receivers is empty I want to hold it, until another receiver will be added to the list and continue to the next request.
I am not sure how to send each 'promise' separately one after another-
exports.notificationListener=functions.database.ref('requests')
.onWrite(event=>{
const ref = event.data.ref;
let requests= [];
var query=ref.orderByChild('TimeStamp');
query.once('value',function(snap){
snap.forEach(request=>{
if(request.val().Status==OPEN)
requests.push(request.key);
});
});
for (let key of requests) {
return getOnlinReceiversToken().then(token=>{
let msg = {
data: {
source: key
}
};
return admin.messaging().sendToDevice(token, msg);
)};
}
});
function getOnlinReceiversToken() {
let result = new Promise((resolve, reject) => {
receiverRef.once('value', (snap) => {
resolve(snap);
},
(err) => {
reject(err);
});
});
return result.then(snap => {
snap.forEach(child => {
if(child.Status == ONLINE){
let token = helper.getToken(child.key,db);
break;
}
});
return token;
});
}

try something like this
var promisesArray = [];
for (let key of requests) {
var currentPromise = getOnlinReceiversToken().then(token=>{
let msg = {
data: {
source: key
}
};
return admin.messaging().sendToDevice(token, msg);
});
promisesArray.push(currentPromise);
}
return Promise.all(promisesArray);

You could use a function that calls itself to iterate through the promises sequentially to send them one after the other
function runPromise(index) {
// jump out of loop if there are no more requests
if (index >= requests.length) {
return console.log('finished!');
}
return getOnlinReceiversToken().then((token) => {
let msg = {
data: { source: key }
};
admin.messaging().sendToDevice(token, msg);
// iterate to the next item
return runPromise(index + 1);
}).catch((err) => {
// break out of loop when an error occurs
console.log(err);
});
}
runPromise(0);

Related

Get and return value outside function - Nodejs

I'm working on some method that have a callback with value, that I need to return and response, but value is null outside callback funciton
How can I solve?
async function createChannel(req) {
let user_id = req.query.user_id;
let chat_name = req.query.chat_name;
var chimesdkmessaging = new AWS.ChimeSDKMessaging({region: region});
var channel = null;
try {
let dateNow = new Date();
const params = {
Name: chat_name,
AppInstanceArn: config.aws_chime_app_istance,
ClientRequestToken: dateNow.getHours().toString() + dateNow.getMinutes().toString(),
ChimeBearer: await AppInstanceUserArn(user_id),
AppInstanceUserArn of the user making the API call.
Mode: 'RESTRICTED',
Privacy: 'PRIVATE'
};
chimesdkmessaging.createChannel(params, function (err, data) {
if (err) {
console.log(err, err.stack);
} // an error occurred
else {
console.log(data); // successful response
console.log('Assegno il canale: ' + data.ChannelArn);
channel = data.ChannelArn; // <----- VALUE I WANT TO RETURN OUTSIDE
}
});
} catch (e) {
return response({error: e}, 500);
}
return response({data: channel},200); // <---- but this channel is null
}
wrap with Promise
let user_id = req.query.user_id;
let chat_name = req.query.chat_name;
var chimesdkmessaging = new AWS.ChimeSDKMessaging({region: region});
let channel = null;
try {
let dateNow = new Date();
const params = {
Name: chat_name,
AppInstanceArn: config.aws_chime_app_istance,
ClientRequestToken: dateNow.getHours().toString() + dateNow.getMinutes().toString(),
ChimeBearer: await AppInstanceUserArn(user_id),
AppInstanceUserArn of the user making the API call.
Mode: 'RESTRICTED',
Privacy: 'PRIVATE'
};
channel = await new Promise((resolve,reject)=>{
chimesdkmessaging.createChannel(params, function (err, data) {
if (err) {
console.log(err, err.stack);
reject(err)
} // an error occurred
else {
console.log(data); // successful response
console.log('Assegno il canale: ' + data.ChannelArn);
resolve(data.ChannelArn)// <----- VALUE I WANT TO RETURN OUTSIDE
}
});
})
} catch (e) {
return response({error: e}, 500);
}
return response({data: channel},200); // <---- but this channel is null
}

How to avoid DEADLINE_EXCEEDED parsing firestore database to send push notifications?

I'm trying to send push notifications to 10 000+ tokens stored in Firestore.
Each time, I got a DEADLINE_EXCEEDED error.
I have already updated the timeout to 300s.
I don't know how to improve my code to avoid this error.
const pagination = 200;
function parseAndSend(push) {
const parse = async(request, total) => {
let snapshot = await request.get();
let lastVisible = snapshot.docs[snapshot.docs.length-1];
let length = snapshot.size;
var users= {};
snapshot.forEach((doc) => {
pushHelperFunctions.addUsers(doc, users);
});
pushHelperFunctions.sendMessage(users, push);
if(length < pagination) {
return console.log("finish", total+length);
} else {
let next = admin.firestore().collection("users").startAfter(lastVisible).limit(pagination);
return parse(next, total+length);
}
}
return parse(admin.firestore().collection("users").limit(pagination), 0);
}
async function sendMessage(users, push) {
Object.keys(users).forEach(key => {
if (users[key].length > 0) {
let message = payloads.setMessage(users[key], key, push);
// Send notifications to all tokens.
admin.messaging().sendMulticast(message).then(response => {
console.log(response.successCount + " messages were sent successfully");
cleanupTokens(response, users[key]);
return true;
}).catch(error => {
mailer.sendMail("messaging().sendToDevice - Error sending message "+push);
console.log("Error sending message:", error);
return false;
});
}
});
}
// Cleans up the tokens that are no longer valid.
function cleanupTokens(response, tokens) {
// For each notification we check if there was an error.
if (response.failureCount > 0) {
const tokensDelete = [];
const failedTokens = [];
response.responses.forEach((resp, index) => {
if (!resp.success) {
failedTokens.push(tokens[index]);
const deleteTask = admin.firestore().collection('users').doc(tokens[index]).delete();
tokensDelete.push(deleteTask);
}
});
console.log('List of tokens that caused failures: ' + failedTokens);
return Promise.all(tokensDelete);
} else {
return null;
}
}
Also I don't find a way to test my code without sending a prod message to my users... It makes testing difficult.

use async - await with socket.io nodejs

I'm developing a web application using nodejs socket.io and angular 9. In my backend code I have written sockets in socket.connect.service.
Follows is a socket I'm using
socket.on('request-to-sit-on-the-table', async function (data, callback) { //Previously Register
let table = persistence.getTable(tableToken);
if (typeof table === 'undefined') {
let errMsg = 'This table does not exists or already closed.'
callback(prepareResponse({}, errMsg, new Error(errMsg)));
return;
}
//TODO: Get the displayName from the token.
let guest = await guestUserService.getGuestUserByPlayerToken(JSON.parse(data.userToken));***//Here is the issue***
// let displayName = 'DisplayName-' + guest;
let displayName = 'DisplayName-' + Math.random();
//TODO: Check whether the seat is available
// If the new screen name is not an empty string
let isPlayerInCurrentTable = persistence.isPlayerInCurrentTable(tableToken, userToken);
if (displayName && !isPlayerInCurrentTable) {
var nameExists = false;
let currentPlayersTokenArr = persistence.getTableObjectPlayersToken(table)
for (var token in currentPlayersTokenArr) {
let gamePlayer = persistence.getPlayerPlayer(currentPlayersTokenArr[token])
if (typeof gamePlayer !== "undefined" && gamePlayer.public.name === displayName) {
nameExists = true;
break;
}
}
if (!nameExists) {
//Emit event to inform the admin for requesting to sit on the table.
let ownerToken = persistence.getTableObjectOwnerToken(table);
let ownerSocket = persistence.getPlayerSocket(ownerToken);
ownerSocket.emit('requested-to-sit', {
seat: data.seat,
secondaryUserToken: userToken,
displayName,
numberOfChips: envConfig.defaultNumberOfChips
});
callback(prepareResponse({userToken}, 'Player connected successfully.'));
} else {
callback(prepareResponse({}, 'This name is already taken'));
}
} else {
callback(prepareResponse({}, 'This user has already joined to a game. Try clear caching'));
}
});
In my code I'm getting data from another code in guest.user.service. But I get undefined to the value of "guest"
Follows are the methods I have used in guest.user.service
exports.findById = (id) => {
return new Promise(function(resolve, reject) {
guestUserModel.findById(id, (err, data) =>{
if(err){
reject(err);
} else {
resolve(data);
}
});
});
};
exports.getGuestUserByPlayerToken = (playerToken) => {
var player = playerService.findOne({ token: playerToken })
.then(function (data) {
return self.findById(data.guestUser._id.toString());
})
.then(function (guestUser) {
return guestUser.displayName;
})
.catch(function (err) {
throw new Error(err);
})
};
Although I get my displayName for the return value It is not passed to the "guest" in my socket.Is there any syntax issue to get data as I'm using promises.please help
exports.getGuestUserByPlayerToken = async playerToken => {
try {
let player = await playerService.findOne({token:playerToken});
return playerService.findById(player.guestUser._id)
} catch(error) {
console.log(error);
return null;
}
};
This is just handle error on awaited promise not returned one. You need to handle that in caller side.

How to return 2 arrays after saving data to mongodb using node js

I need help with code below. I get an array of items from the client then the goal is to save them in mongodb and return the list classified as 'saved' and 'failed' items. sample of failed items are those that are duplicate on a unique attribute.
I know the code below will not work because of variable scope. how do i get around it? the code below returns an empty array for both savedItems and failedItems. Thanks!
router.post('/addItems', async (req, res, next) => {
let items = req.body;
let result = {
savedItems: [],
failedItems: []
};
function saveData() {
for (i = 0; i < items.length; i++) {
item = items[i];
Model.create({ ...item }, (err, data) => {
if (err) {
result.failedItems.push(item);
} else {
result.savedItems.push(item);
}
});
}
return result;
}
saveData().then(result => {
res.send({
results: result
});
});
});
router.post('/addItems', async (req, res, next) => {
// use try catch when use async
try {
let items = req.body;
let result = {
savedItems: [],
failedItems: []
};
for (let i = 0; i < items.length; i++) {
const item = items[i];
// use the returned promise instead of callback for Model.create
const data = await Model.create({ ...item });
result.savedItems.push(item);
// if also need to handle failed item in result use anathor try catch inside
/*try {
const data = await Model.create({ ...item });
result.savedItems.push(item);
} catch( err ) {
result.failedItems.push(item);
}*/
}
res.send({
results: result
});
} catch( err ) {
// To all the errors unexpected errors + thrown rejected promises
res.send({
error: err
});
}
});
Your saveData method didn't return a promise, try this
function saveData() {
return new Promise(resolve => {
let items = req.body;
let result = {
savedItems: [],
failedItems: []
};
let promises = [];
for (i = 0; i < items.length; i++) {
item = items[i];
let promise = new Promise(resolve => {
Model.create({ ...item }, (err, data) => {
if (err) {
result.failedItems.push(item);
} else {
result.savedItems.push(item);
}
resolve();
});
});
promises.push(promise);
}
Promise.all(promises).then(() => resolve(result));
})
}

Return a value from function inside promise

I am trying to return the array shiftInfo from the function csData() within a promise.
function crewsense(){
var request = CS.find({});
request
.then(result => {
var created = result[0].created,
currentTime = moment(),
diff = (currentTime - created);
if(diff < 84600000){
console.log("Current Token is Valid");
var access_token = result[0].access_token;
console.log('Obtaining Crewsense Shift Data');
return access_token
}else{
console.log("Current Token is invalid. Updating Token");
csToken();
}
}).then(access_token => {
csData(access_token) //I am trying to get this function to return async data.
}).then(shiftInfo => { //I want to use the data here.
})
Here is the csData function:
function csData(csKey) {
const dayURL = {
method: 'get',
url: 'https://api.crewsense.com/v1/schedule?start='+today+'%2007:30:00&end='+tomorrow+'%2007:30:00',
headers:{
Authorization: csKey,
}
}
const request = axios(dayURL)
request
.then(result => {
var shiftInfo = [];
var thisShift = [];
var onDuty = result.data.days[moment().format("YYYY-MM-DD")].assignments;
thisShift.push(result.data.days[moment().format("YYYY-MM-DD")].day_color);
var persons = [];
var i = 0;
for(var i=0; i<onDuty.length; i++){
let station = onDuty[i].name
for(var x=0; x<onDuty[i].shifts.length; x++){
var person = {
name: onDuty[i].shifts[x].user.name,
position: onDuty[i].shifts[x].qualifiers[0].name,
station: station
}
persons.push(person);
}
}
shiftInfo = [{thisShift}, {persons}];
// console.log(shiftInfo)
return shiftInfo
})
.catch(error => console.error('csData error:', error))
}
I have attempted assigning var shiftInfo = csData(access_token) w/o success and several other ways to call the csData function. I have attempted reading other like problems on here and I have just ended up confused. If someone can point me in the right direction or please point out the fix I might be able to get it to click in my head.
I appreciate everyone's time.
Thanks!
Whatever you return inside a then, will be passed to the next then callback. If you return a Promise, the result of the promise will be sent to the next then callback:
new Promise((resolve) => {
// We resolve to the value we want
resolve("yay");
}).then((value) => {
// In the first then, value will be "yay"
console.log("First then:", value);
// Then we return a new value "yay x2"
return value + " x2";
}).then((value) => {
// In this second then, we received "yay x2"
console.log("Second then:", value);
// Then we return a promise that will resolve to "yay x2 again"
return new Promise((resolve) => {
setTimeout(() => {
resolve(value + " again");
}, 1000);
});
}).then((value) => {
// After a second (when the returned Promise is resolved) we get the new value "yay x2 again"
console.log("Third then:", value);
// And now we return a Promise that will reject
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error("wtf"));
}, 1000);
});
}).catch((error) => {
// This catch on the whole promise chain will catch any promise rejected
console.log(error.toString());
});
So simply csData must return the promise is creating, and you need to return that promise to the then callback you want:
[...]
}).then(access_token => {
return csData(access_token) //I am trying to get this function to return async data.
}).then(shiftInfo => { //I want to use the data here.
console.log(shiftInfo);
}).catch((err) => {
// Whatever...
});
function csData(csKey) {
[...]
return request.then(result => {
[...]
}
Because you are returning a promise, I recommend you to add the catch outside csData and add it to the promise chain you have before.

Resources