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)
})()
Related
I am fairly new to Node.JS, but have some experience in other languages. I am trying to achieve the following:
I want to perform a task and if it fails perform another task.
I have two files: one is the main function, the other contains the class.
First the main function (main.js):
(async function main() {
let { MyClass } = require("./my_class.js");
const mc = new MyClass();
await mc.do_stuff();
console.log(mc.message);
})();
The other is the class (my_class.js)
class MyClass {
constructor() {
this.message='hello';
}
do_stuff=async function() {
return new Promise((resolve,reject) => async function (){
let [res,rej]=await do_first('fail');
if(rej) {
console.log('do_first() failed.');
[res,rej]=await do_second('succeed');
if(rej) {
console.log('do_second() failed.');
reject('failed');
} else {
console.log('do_second() succeeded.');
resolve('success');
}
} else {
console.log('do_first() succeeded, no call to do_second().');
resolve('success');
}
});
}
do_first=async function(param) {
return new Promise((resolve,reject) => {
if(param==='fail') {
console.log('rejecting do_first()');
reject('failure');
} else {
console.log('resolving do_first()');
resole('success');
}
});
}
do_second=async function(param) {
return new Promise((resolve,reject) => {
if(param==='fail') {
console.log('rejecting do_second()');
reject('failure');
} else {
console.log('resolving do_second()');
resole('success');
}
});
}
}
exports.MyClass = MyClass
If I try to run it with node ./main.js nothing happens. If I run mc.do_stuff() without the await, I do get the hello... Which boats am I missing?
For that matter: I am running NodeJS v18.12.0
A few things need to be changed here to make it work:
When using await, only the Promise.resolve comes back to regular code execution. Promise.reject always raises an Exception.
Typo resole
changed code to consistently use arrow syntax. This requires referencing functions as instance members -> this.do_first. I suppose this was your intention. The syntax before did not execute the function as part of the object, but in global scope.
Here's your code in a working state
class MyClass {
constructor() {
this.message = "hello";
}
do_stuff = async () => {
return new Promise(async (resolve, reject) => {
try {
await this.do_first("fail");
console.log("do_first() succeeded, no call to do_second().");
resolve("success");
} catch (err) {
console.log("do_first() failed.");
try {
await this.do_second("succeed");
console.log("do_second() succeeded.");
resolve("success");
} catch (err) {
console.log("do_second() failed.");
reject("failed");
}
}
});
};
do_first = async (param) => {
return new Promise(async (resolve, reject) => {
if (param === "fail") {
console.log("rejecting do_first()");
reject("failure");
} else {
console.log("resolving do_first()");
resolve("success");
}
});
};
do_second = async (param) => {
return new Promise((resolve, reject) => {
if (param === "fail") {
console.log("rejecting do_second()");
reject("failure");
} else {
console.log("resolving do_second()");
resolve("success");
}
});
};
}
exports.MyClass = MyClass;
PS C:\Users\patrick\Documents\GitHub\stackoverflow-74714360> node ./main.js
rejecting do_first()
do_first() failed.
resolving do_second()
do_second() succeeded.
hello
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);
}
I have some problems with the multiple promises in my code. There is no way to return to items who are not in the database. I changed the code multiple times but no luck. The only data it returns is "datas": [
null,
null
]
This is my code
var start = function(offset, entry) {
return new Promise(function(resolve, reject) {
rp('************' + entry).then(function(repos) {
resolve(repos);
}).catch(function(err) {
reject(err);
});
});
};
var findnewones = function(iten) {
return new Promise(function(resolve, reject) {
return Promise.all(iten.items.map(function(ndtrcitem) {
return new Promise(function(resolve, reject) {
Items.findOne({"metadata.trcid": ndtrcitem.metadata.trcid}).exec(function(err, doc) {
if (!doc) {
resolve(ndtrcitem);
}
});
})
})).then(datas => {
resolve(datas);
});
})
}
exports.find = function(req, res, next) {
var ndite = ["locations", "events"];
var items = [];
return Promise.all(ndite.map(function(entry) {
return start(0, entry).then(function(res) {
for (i = 0; i <= res.count; i += 10) {
return start(i, entry).then(function(iten) {
findnewones(iten).then(function(dat) {
items.push(dat);
});
});
}
return items;
})
})).then(datas => {
res.json({datas});
});
}
I think because the for loop there is synchronous and it's not waiting for the start() promise to resolve.
for (i = 0; i <= res.count; i += 10) {
return start(i, entry).then(function(iten) {
findnewones(iten).then(function(dat) {
items.push(dat);
});
});
}
I have replaced it with async/await, don't know if it will work right away, I am just providing you with a hint in this very complicated promise chain. If it or any variation of it works please update this answer.
exports.find = function (req, res, next) {
var ndite = ["locations", "events"];
var items = [];
return Promise.all(ndite.map(function (entry) {
return start(0, entry)
.then(async function (res) {////// this
for (i = 0; i <= res.count; i += 10) {
await start(i, entry).then(function (iten) { ////this
findnewones(iten).then(function (dat) {
items.push(dat);
});
});
}
return items;
})
})).then(datas => {
res.json({
datas
});
});
}
One of the biggest issue we face now with parse-server is duplication. Although we have implemented a Parse cloud code to prevent such event through beforeSave and afterSave methods at the same time added external middleware to check for existing object before saving still we face duplication over and over specially on concurrent operations.
Here is our code to prevent duplication for a specific class:
Parse.Cloud.beforeSave("Category", function(request, response) {
var newCategory = request.object;
var name = newCategory.get("name");
var query = new Parse.Query("Category");
query.equalTo("name", name);
query.first({
success: function(results) {
if(results) {
if (!request.object.isNew()) { // allow updates
response.success();
} else {
response.error({errorCode:400,errorMsg:"Category already exist"});
}
} else {
response.success();
}
},
error: function(error) {
response.success();
}
});
});
Parse.Cloud.afterSave("Category", function(request) {
var query = new Parse.Query("Category");
query.equalTo("name", request.object.get("name"));
query.ascending("createdAt");
query.find({
success:function(results) {
if (results && results.length > 1) {
for(var i = (results.length - 1); i > 0 ; i--) {
results[i].destroy();
}
}
else {
// No duplicates
}
},
error:function(error) {
}
});
});
This code above is able to prevent some duplicate but most still goes through, example:
What is the "ultimate way" to prevent duplication with Parse server?
You can always create a unique index in mongodb for the field that should be unique in your document.
This way any save that conflicts with that index, will be aborted
Maybe you should write something with Promises like :
Parse.Cloud.beforeSave("Category", function (request, response) {
return new Promise((resolve, reject) => {
var query = new Parse.Query("Category");
query.equalTo("name", "Dummy");
return query.first().then(function (results) {
resolve(); // or reject()
});
})
});
Parse.Cloud.beforeSave("Category", async (request) => {
(...)
await results = query.first();
// then your logic here
response.success();
response.error({ errorCode: 400, errorMsg: "Category already exist" })
})
Here is my Solution:
Parse.Cloud.beforeSave( 'ClassName', async ( request ) => {
const columnName = 'columnName'
const className = 'ClassName'
if( request.object.isNew() ) {
var newCategory = request.object
var name = newCategory.get( columnName )
var query = new Parse.Query( className )
query.equalTo( columnName, name )
const results = await query.count()
if( results === 0 ) {
// no response.success needed
// https://github.com/parse-community/parse-server/blob/alpha/3.0.0.md
} else {
throw 'Is not unique';
}
}
} )
I am trying to load a hierarchy in my database. I have a column with parentId in my table so every row can have a parent. But I am having problems using recursion and promises.
function read (options) {
return serviceItemAttributeModel.findOne({
id: options.id,
id_organization: options.idOrganization
})
.then((attribute) => {
if (attribute) {
return loadChildren(attribute, attribute);
} else {
return attribute;
}
});
}
function loadChildren (root, attribute) {
return serviceItemAttributeModel.findAll({
where: {
id_parent: attribute.id
}
})
.then((attributes) => {
if (!attributes) {
return root;
} else {
attribute.serviceItemAttributes = [];
attributes.forEach(function (each) {
attribute.serviceItemAttributes.push(each);
return loadChildren(root, each);
});
}
});
}
So, I call read that calls loadChildren to recursively try to load all entities (by looking children of an entity) and I get an undefined value. Any ideas?
I am also getting an error on console: a promise was created in a handler but was not returned from it.
EDIT:
Came up if this solution after Nosyara help. thanks!:
function read (options) {
return serviceItemAttributeModel.findOne({
where: {
id: options.attributeId,
id_organization: options.idOrganization
}
})
.then((attribute) => {
if (!attribute) {
return new Promise(function (resolve, reject) {
resolve(attribute);
});
} else {
return new Promise(function (resolve, reject) {
attribute.queryCount = 1;
resolve(attribute);
})
.then((attribute) => loadChildren(attribute, attribute));
}
});
}
function loadChildren (root, attribute) {
return new Promise(function (resolve, reject) {
return serviceItemAttributeModel.findAll({
where: {
id_parent: attribute.id
}
})
.then((attributes) => {
attributes.length = attributes.length || 0;
root.queryCount = root.queryCount - 1 + attributes.length;
if (root.queryCount === 0) {
resolve(root);
} else if (root.queryCount > 10) {
let error = new Error('Service attribute hierarchy cant have more then 10 levels');
error.statusCode = 500;
reject(error);
} else {
attribute.serviceItemAttributes = [];
attributes.forEach(function (each) {
attribute.serviceItemAttributes.push(each);
return loadChildren(root, each).then(() => {
resolve(root);
});
});
}
});
});
}
You messing up with async calls and returns. You can convert both function to async, and pass through result structure to be updated. Example:
function read(...) {
return new Promise(function (accept, reject) {
// You code goes here, but instead of return
accept(resultFromAsyncFunction);
});
}
// ...
read(...).then(function(resultData) { ... });
Here is example of Promise recursion.