Herarchy query using sequelize / nodejs - node.js

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.

Related

Nodejs - class with async await does nothing

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

multiple promises in api server node returns null

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

Is it possible to bind this in bluebird map?

So I've tried to write up an example as best I could of what I'm trying to do, this isn't a very practical example but I tried to simplify it, but I feel like I may have complicated this in trying to make an example.
class add {
constructor(baseValue) {
this.base = baseValue;
return new Promise((resolve, reject) => {
resolve(this);
});
}
addBase(num) {
return new Promise((resolve, reject) => {
resolve(this.base + num);
});
}
}
const values = [1,2,3,4,5];
Promise.try(() => {
return new add(5);
}).then((add) => {
// we want to find if a 5 exists in the results
const addPromise = Promise.resolve(values).map(add.addBase, {concurrency: 1});
return Promise.try(() => {
return addPromise;
}).then((results) => {
for(let i = 0; i < results.length; i++) {
if(results[i] === 10) {
return i;
}
}
// doesn't exist
return null;
});
}).then((result) => {
if(result === null) {
console.log('10 does not exist');
} else {
console.log('10 is at position ' + result);
}
})
<script src="https://cdn.jsdelivr.net/bluebird/latest/bluebird.min.js"></script>
If you run this you'll get an error that you can't get base of undefined, this is because of the mapping in bluebird. const addPromise = Promise.resolve(values).map(add.addBase, {concurrency: 1}); Is there a way on this line to bind the add object to this when making these calls?
This is actually a little simpler than you're making it I think. You are passing the raw function into map(), but you should probably be passing an arrow function instead. Consider this simple class and code that tries to use map() by passing add():
class Test{
constructor(n) {
this.n = n
}
add(k) {
return this.n + k
}
}
let t = new Test(10)
let arr = [1, 2, 3]
// error TypeError: undefined is not an object (evaluating 'this.n')
arr.map(t.add)
This throws an error because map isn't calling add() from the object, it just thinks it's a function. An easy fix is to call map like this:
class Test {
constructor(n) {
this.n = n
}
add(k) {
return this.n + k
}
}
let t = new Test(10)
let arr = [1, 2, 3]
let mapped = arr.map((n) => t.add(n))
console.log(mapped)
You could also use:
let mapped = arr.map(t.add.bind(t))
but to me that's harder to read and understand quickly. I'm not sure what's going on with all the immediately resolved promises in your code, but changing the way you call map() makes that error go away. (there's another error later where you reference i that's not in scope.)
You've pretty much answered your own question…
class add {
constructor(baseValue) {
this.base = baseValue;
return new Promise((resolve, reject) => {
resolve(this);
});
}
addBase(num) {
return new Promise((resolve, reject) => {
resolve(this.base + num);
});
}
}
const values = [1,2,3,4,5];
Promise.try(() => {
return new add(5);
}).then((add) => {
// we want to find if a 5 exists in the results
const addPromise = Promise.resolve(values).map(add.addBase.bind(add), {concurrency: 1});
return Promise.try(() => {
return addPromise;
}).then((results) => {
for(let i = 0; i < results.length; i++) {
if(results[i] === 10) {
return i;
}
}
// doesn't exist
return null;
});
}).then((result) => {
if(result === null) {
console.log('10 does not exist');
} else {
console.log('10 is at position ' + result);
}
})
<script src="https://cdn.jsdelivr.net/bluebird/latest/bluebird.min.js"></script>

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)
})()

Promise looping over nested arrays

I'm wrestling with nested promise loops and having trouble finding a working solution.
I looked around and found this: https://stackoverflow.com/a/29396005/3560729
The promiseWhile function seems to have what I need but I'm having trouble getting the nesting to return to the outer loop
promiseWhile:
function promiseWhile(predicate, action) {
function loop() {
if (!predicate()) return;
return Promise.resolve(action()).then(loop);
}
return Promise.resolve().then(loop);
}
Nested Loop:
let outerArray = outerArrayOfObjects;
let returnArray = [];
let returnArrayIndex = 0;
let outerIndex = 0;
let outerLength = outerArray.length;
let passObject = { };
promiseWhile(function() {
return outerIndex < outerLength;
}, function() {
let innerIndex = 0;
let innerLength = outerArray[outerIndex].innerArray.length;
passObject = {
innerObject: outerArray[outerIndex].innerArray[innerIndex],
returnArray: returnArray,
returnArrayIndex: returnArrayIndex
};
promiseWhile(function() {
return innerIndex < innerLength;
}, function() {
return new Promise(function(resolve, reject){
Promise.all([
promiseFunction1(innerObject),
promiseFunction2(innerObject),
promiseFunction3(innerObject),
])
.then(function (allMappings) {
passObject.returnArray[returnArrayIndex++] = {
"result1": allMappings[0],
"result2": allMappings[1],
"result3": allMappings[2]
}
offersIndex++;
return resolve(passObject)
})
.catch(function (err) {
offersIndex++;
return reject(err);
})
})
})
outerIndex++;
}).then(function() {
return resolve(passObject);
});
})
}
I think my main questions are: Where do I process the results? How should I pass the values such that the return array is built properly?
The promiseWhile above is good for performing actions but not good for setting values in a nested loop and returning the results.
I ended up going with an approach using bluebird:
var Promise = require('bluebird');
let outerArray = object.outerArray;
let returnArray = [];
let returnIndex = 0;
Promise.map(outerArray, function (outerArrayObject) {
let innerArray = outerArrayObject.innerArray;
let outerArrayValue = outerArrayObject.value;
Promise.map(innerArray, function (innerArrayObject) {
Promise.all([
PromiseFunction1(innerArrayObject),
PromiseFunction2(innerArrayObject),
PromiseFunction3(innerArrayObject),
])
.then(function (allResults) {
returnArray[returnIndex++] = {
"result1": allResults[0],
"result2": allResults[1],
"result3": allResults[2],
"result4": outerArrayValue,
}
return resolve(returnArray);
})
.catch(function (err) {
return reject(err);
})
})
})
.then(function () {
return resolve(returnArray)
}
).catch(function(err){
return reject(err);
}
)
}

Resources