Return true when detect event response - node.js

I am using puppeteers. I have created a page.on('response') that is listening that is listening for requests.
I have a loop that takes care of scrolling. How can I detect if scrolling raises the 'response' event?
I was thinking of returning a boolean from the event, but how could I capture it?
page.on('response', (response) => {
if (response.url().indexOf('page') >= 0) {
return true;
} else {
return false;
}
});
while(items.length < howMuchItems) {
await page.evaluate((sel) => {
window.scrollBy(0, document.scrollingElement.querySelector(sel).scrollHeight);
}, selectors.CONTAINER_SCROLLED);
// Detect if exists event response
// If doesn´t exist => break loop
items= await page.$$(selectors.ITEM);
}

I dont know your program logic, but generally you must create like this code
const waitForNewItems = () =>
new Promise((resolve, reject) =>
page.once('response', (response) => {
if (response.url().indexOf('page') >= 0) {
return resolve(true);
} else {
return resolve(false);
}
});
while(items.length < howMuchItems) {
await page.evaluate((sel) => {
window.scrollBy(0, document.scrollingElement.querySelector(sel).scrollHeight);
}, selectors.CONTAINER_SCROLLED);
// Detect if exists event response
// If doesn´t exist => break loop
const bool = await waitForNewItems();
if (!bool) break;
items= await page.$$(selectors.ITEM);
}

Related

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.

Firebase Cloud Functions - Cannot read property 'forEach' of undefined

I have something like this:
const promises = [];
l = niz.length;
for (i = 0; i < l; i++) {
if(niz[i].length === 0) {
continue;
}
promises.push(admin.messaging().sendToDevice(niz[i], payload, options));
}
return Promise.all(promises).then((response) => {
return cleanupTokens(response, tokens);
//return resolve();
}).then(() => {
return resolve();
});
but always I have crash in firebase logs:
TypeError: Cannot read property 'forEach' of undefined
at cleanupTokens (/user_code/index.js:193:20)
at Promise.all.then (/user_code/index.js:169:20)
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
Response is:
response: [ { results: [ [Object] ],
canonicalRegistrationTokenCount: 0,
failureCount: 0,
successCount: 1,
multicastId: 5591935326806715000 } ]
response: undefined
cleanup is:
function cleanupTokens(response, tokens) {
// For each notification we check if there was an error.
const tokensToRemove = {};
console.log('response: ', response);
console.log('response: ', JSON.stringify(response.results));
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered' ||
error.code === 'messaging/invalid-recipient') {
tokensToRemove[`/tokens/${tokens[index]}/g`] = null;
tokensToRemove[`/tokens/${tokens[index]}/l/0`] = null;
tokensToRemove[`/tokens/${tokens[index]}/l/1`] = null;
}
}
});
return admin.database().ref().update(tokensToRemove);
}
Any help with cleanup tokens?
You chose to send each notification separately to each user, making the response become "wrapped" as a singleton of response, and each response is also a singleton of itself - making it weird to access (response[0].results.foreach should probably work)
But even then, you'd have only 1 item to go trough, because you made each notification to be sent individually. I believe it is much more efficient to have a 1 time sendToDevice() request to all of the relevant tokens, instead of sending them separately.
Consider changing your first code to this:
//const promises = [];
var tokens = [];
l = niz.length;
for (i = 0; i < l; i++) {
//if(niz[i].length === 0) {
if(niz[i].length > 0) //I guess those are your relevant tokens
tokens.push(niz[i]); //Fill in the relevant tokens
}
//promises.push(admin.messaging().sendToDevice(niz[i], payload, options));
return admin.messaging().sendToDevice(tokens, payload, options)
.then(response => {
return cleanupTokens(response, tokens);
})
.then(() => {
return resolve();
});
//return Promise.all(promises).then((response) => {
// return cleanupTokens(response, tokens);
// //return resolve();
//}).then(() => {
// return resolve();
//});
The rest of your code should work after this change.
(Few credits to #Frank for his comment - the JSON log helped determining the issue)
EDIT:
Regarding your comment, if each niz[i] item is an array itself, you could do this:
const promises = [];
l = niz.length;
for (i = 0; i < l; i++) {
if(niz[i].length === 0) {
continue;
//promises.push(admin.messaging().sendToDevice(niz[i], payload, options));
var newPromise = return admin.messaging().sendToDevice(niz[i], payload, options)
.then(response => {
return cleanupTokens(response, tokens);
})
.then(() => {
return resolve();
});
promises.push(newPromise);
}
return Promise.all(promises);
//return Promise.all(promises).then((response) => {
// return cleanupTokens(response, tokens);
// //return resolve();
//}).then(() => {
// return resolve();
//});

how to handle array of 'promis'

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);

How to run same promises one after another NodeJs

I am trying to solve the following problem.
Consider the following case. I need to check if an array of servers is alive. Or to be more specific I need to find the first working server from the provided list, I need to do this one by one.
For example if the first server doesn't work, check another and another ...
As far as NodeJS is asynchronous I cannot do this in a for loop. So I tried to implement something similar to recursion, it looks ugly and doesn't work, but I've tried)
static findWorkingServer(servers, payload) {
return NetworkUtils.getMyPublicIP()
.then((ip) => {
return new Promise(function (resolve, reject) {
let currentIndex = -1;
if (servers.length > 0) {
let currentServer;
let serverCheckCallback = function (result) {
if (result) {
resolve({working: currentServer, payload: payload});
}
else {
if (currentIndex < servers.length-1) {
currentIndex++;
currentServer = servers[currentIndex];
NetworkUtils.checkIfServerWorking(currentServer, ip)
.then(serverCheckCallback);
}
else {
reject(new Error("No working servers found"))
}
}
};
serverCheckCallback(false);
}
else {
resolve(new Error("No servers provided"));
}
})
});
}
static checkIfServerWorking(credentials, publicIp) {
return new Promise(function (resolve, reject) {
if(credentials) {
request({
url: credentials.url,
agentClass: agentClass,
agentOptions: {
// Agent credentials
}
})
.then(res => {
// Do some stuff with resposne
resolve(someCondition);
})
.catch(err => {
resolve(false);
});
}else {
resolve(false);
}
});
}
Please help to get the desired result, maybe it is possible to run requests synchronously.
Could be done with await/async:
let servers = ["test0.com","test1.com","test2.com","test3.com","test4.com"]
class ServerTest {
static async checkServer(name) {
if (name === "test3.com")
return true //returns promise that resolves with true
else
return false //returns promise that resolves with false
}
}
(async()=>{ //IIFE (await can only be used in async functions)
let targetServer
for (i in servers) {
if (await ServerTest.checkServer(servers[i]) === true) {
targetServer = servers[i]
break
}
}
console.log("Found a working server: " + targetServer)
})()

processing items in a array with delay between each item in nodejs

I have a list of messages that needs to send with 1 second delay in between.
currently I am using this function :
send() {
return new Promise((resolve, reject) => {
this._send(resolve);
});
}
_send(resolve, index) {
index = index || 0;
if (this.messages.length && index < this.messages.length) {
if (this.messages[index]) {
let response = this.messages[index];
response.send().then(() => {
this._schedule(resolve, index);
});
} else
this._schedule(resolve, index);
}
else
resolve();
}
_schedule(resolve, index) {
setTimeout(() => {
this._send(resolve, ++index);
}, 1000);
}
Is this s good approach? I have noticed that node is using more ram than usual when this section is running.
I am using Bluebird for Promise.
UPDATE based on #Roamer's comment:
Based on docs :
Promise.reduce(
Iterable<any>|Promise<Iterable<any>> input,
function(any accumulator, any item, int index, int length) reducer,
[any initialValue]
) -> Promise
_wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
__send() {
return Promise.reduce(this.messages, function (dummy, response, index, len) {
return response.send().then(() => {
return this._wait(1000);
}).catch(() => {
//don't care about a error , continue to the next one
return this._wait(1000);
});
}).then(() => {
return true;
});
}
Is this correct?

Resources