Promising gets stuck when doing actions on the data - javascript

I have a problem with promises. I have to do two queries to the database and I have to use then to read them. After that I have to merge them. What I did is as follows:
console.log("Start");
obj.find(query1).then(function(docs1) {
console.log("HERE0");
obj.find(query2).then(function(docs2) {
// Merge reports
console.log("HERE1");
console.log("docs1");
console.log("docs2");
c = [...docs1.files, ...docs2.files];
console.log("HERE2");
for (let i = 0; i < c.length; i++) {
for (let j = i + 1; j < c.length; j++) {
console.log("i = " + i + " j = " + j);
if(c[i].name == c[j].name) {
c[i].totals.invalid += c[j].totals.invalid ;
c[i].totals.valid += c[j].totals.valid ;
c[i].totals.total += c[j].totals.total;
c[i].totals.percentage = (c[i].totals.invalid / c[i].totals.valid) * 100;
c.splice(j, 1)
}
}
}
console.log("DONE");
response.send(c);
}, function (err) {
logger.error(err.message);
response.status(500).send(err.message);
});
}, function (err) {
logger.error(err.message);
response.status(500).send(err.message);
});
Output:
Start
HERE0
HERE1
<valid docs1>
<valid docs2>
After he gets to the line c = [...docs1.files, ...docs2.files]; it's get stuck (doesn't get to the next prints) and I don't understand why. I can see those docs being printed to the console.log but why it does not work on that line?
Also, is there a better way of of performing the find call so there will be one nesting instead of two (one .then)?
EDIT: the find method uses Q.defer();. Maybe I need to use it too?

You can use Promise.all with obj.find(query1) and obj.find(query2) expressions and later use them in thenable callback.
As far as merging function is concerned I prefer to use reduce and find for this example
Take a look at the snippet with mocked values.
const random = (from, to) => Math.floor(Math.random() * (to - from) + from);
const randomObj = (id) => {
const name = `sample ${id}`;
const invalid = random(1, 6);
const valid = random(6, 12);
const percentage = invalid / valid * 100;
return {
name,
totals: {
invalid,
valid,
percentage
}
}
}
const obj = {
find: (query) => Promise.resolve(Array(random(1, 4)).fill(0).map((pr, index) => randomObj(index + 1)))
}
const query1 = ""
const query2 = ""
Promise.all([obj.find(query1), obj.find(query2)]).then(results => {
let [list, secondList] = results;
console.log('first list', list);
console.log('second list', secondList);
const result = list.reduce((acc, record) => {
const item = secondList.find(pr => pr.name === record.name);
if (item) {
record.totals.invalid += item.totals.invalid;
record.totals.valid += item.totals.valid;
record.totals.percentage = record.totals.invalid / record.totals.valid * 100;
}
acc = [...acc, record];
return acc;
}, [...secondList.filter(pr => !list.some(npr => npr.name === pr.name))])
console.log('result', result);
})

Related

Call multiple async functions within a single app response - Express

What's the correct way to call multiple async functions and send their response in express ?
I tried to use try / catch, await, but nothing works. This is my problem :
When I do this
app.get('/api/caristes/reappro', async (req, res) => {
const a = await a()
const b= await b()
res.send({reappro: a, caristes: b})
})
The data b send by the server is not correct. But I tested the function b again and again and it's working perfectly. Then, I made request to my API and I found values are different than expected, so I deduced my problem is from my server request. The data of function b seems to not be updated every time the server is called.
For the other links, I send data like this :
a().then(result=> {
res.send(result)
})
and it's working perfectly. But I didn't manage to use multiple .then with multiple async functions : it returns an error (undefined value).
Any suggestions ?
EDIT : the two functions
function i named b :
const {alleesCircuit} = require('./alleesCircuit')
const {caristes} = require('./caristes');
const {lstMvtCariste} = require('./lstMvtCariste')
async function caristesAlleesHeure () {
const caristesActifs = await caristes()
const alleesCircuits = await alleesCircuit()
const dernierMvt = await lstMvtCariste()
for (let i in caristesActifs) {
let tmp = []
for (let j in caristesActifs[i].CIRCUITS) {
for (let k in alleesCircuits) {
if (Object.values(alleesCircuits[k]).includes(caristesActifs[i].CIRCUITS[j].toString())) {
tmp = [...tmp, ...alleesCircuits[k].ALLPIC]
}
}
}
const alleesCariste = [...new Set(tmp)]
caristesActifs[i].ALLEES = alleesCariste
}
for (let i in caristesActifs) {
// Tri par ordre croissant des allées attribuées aux caristes
caristesActifs[i].ALLEES.sort(function(a,b) {
return a - b
})
// Add zeros to have 21 values
for (let j = 0; j <= 20; j++) {
if(caristesActifs[i].ALLEES[j] !== j+1) {
(caristesActifs[i].ALLEES).splice(j, 0, 0)
}
}
// Replace all values !== 0 by 1
for (k in caristesActifs[i].ALLEES) {
if(caristesActifs[i].ALLEES[k] !== 0) {
caristesActifs[i].ALLEES[k] = 1
}
}
}
let caristesAlleesHeure = []
for(let i = 0; i < caristesActifs.length; i++) {
caristesAlleesHeure.push({
...caristesActifs[i],
...(dernierMvt.find((codeUser) => codeUser.CODUTI === caristesActifs[i].CODCAR))
})
}
return caristesAlleesHeure
}
exports.caristesAlleesHeure = caristesAlleesHeure
We don't care about the function a because the server returns correct values of it

Write a higher-order function, checkConsistentOutput()

This function should have two parameters: a function and a value. It should call the argument function with the value two times. If the callback function produces the same result twice, it should return the result of the function call, otherwise, it should return the string 'This function returned inconsistent results'
const checkThatTwoPlusTwoEqualsFourAMillionTimes = () => {
for(let i = 1; i <= 1000000; i++) {
if ( (2 + 2) != 4) {
console.log('Something has gone very wrong :( ');
}
}
};
const addTwo = num => num + 2;
const timeFuncRuntime = funcParameter => {
let t1 = Date.now();
funcParameter();
let t2 = Date.now();
return t2 - t1;
};
// Write your code below
const time2p2 = timeFuncRuntime(checkThatTwoPlusTwoEqualsFourAMillionTimes);
const checkConsistentOutput(func, val) => {
let checkOne = func(val);
let checkTwo = func(val);
if (checkOne === checkTwo){
return checkOne;
} else {
return 'This function returned inconsisent results'
}
}
I'm getting the error SyntaxError: Missing initializer in const declaration. please help me understand.
I'm seeing an error in the last const declaration that appears to be because it's missing an '='.
const checkConsistentOutput(func, val) => {
...
}
const checkConsistentOutput = (func, val) => {
...
}

Variable not persisting when using a constructor

So I'm using a constructor like this
const RPNCalculator = function () {
let methods = {
numberList: [],
calc: 0,
push(num) {
this.numberList.push(num);
},
plus() {
for (let i = 0; i <= this.numberList.length; i++) {
console.log('before:' + this.calc);
this.calc = this.calc + this.numberList[i];
}
console.log('after:' + this.calc);
this.numberList = [];
}
};
return methods;
}
const rpnCalculatorInstance = new RPNCalculator;
The fist console.log prints correctly and adds the elements but the second console.log prints NaN. I've used this pattern before with Object.create but for some reason the this.calc variable isn't persisting when using a constructor.
Any help is appreciated!
you can use reduce to sum up an array https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
run snippet below
class RpnCalculator{
constructor(){
this.numberList = [];
this.push = (num) => { this.numberList = [...this.numberList, num ]}
this.sum = () => {return this.numberList.reduce(( a, c) => a + c, 0);}
}
}
const rpnCalculator = new RpnCalculator();
rpnCalculator.push(1)
rpnCalculator.push(2)
rpnCalculator.push(3)
console.log(rpnCalculator.sum());
Apparently with the dataset I was given the last item in the array was an undefined element. I fixed it by using
if (typeof (this.numberList[i]) === 'number') {
console.log('before:' + this.calc);
this.calc = this.calc + this.numberList[i];
}

What's wrong with this combination-generating recursion?

I have the following code:
const findMult_3 = (num) => {
const powerset = (set) => {
const combinations = []
const combine = (prefix, chars) => {
for (let i = 0; i < chars.length; i++) {
combinations.push(prefix + chars[i])
combine(prefix + chars[i], chars.slice(i + 1))
}
}
combine('', set)
return combinations
}
const allCombinations = powerset(num.toString().split(''))
console.log(allCombinations)
}
findMult_3(362)
I would expect this to work, however, with the input of 362, the function console logs:
[ '3', '36', '362', '32', '6', '62', '2' ]
It's missing variants like 63, 23, 26 etc. It seems the slice call is to blame?
Still not 100% sure what the issue was with the slice call, but I fixed it by sidestepping the issue and avoiding mutating my arrays:
const findMult_3 = (num) => {
const powerset = (set) => {
const combinations = []
const combine = (prefix, chars) => {
for (let i = 0; i < chars.length; i++) {
combinations.push(prefix + chars[i])
combine(prefix + chars[i], chars.filter((x, ind) => ind !== i))
}
}
combine('', set)
return combinations
}
const allCombinations = powerset(num.toString().split(''))
console.log(allCombinations)
}
findMult_3(362)
Note the use of filter instead of splice, maintaining immutability.
I found the problem, duplicate the array (to c), and then remove the element you are combining with:
const findMult_3 = (num) => {
const powerset = (set) => {
const combinations = []
const combine = (prefix, chars) => {
console.log(chars);
for (let i = 0; i < chars.length; i++) {
combinations.push(prefix + chars[i])
var c = chars.slice();
c.splice(i,1);
combine(prefix + chars[i], c);
}
}
combine('', set)
return combinations
}
const allCombinations = powerset(num.toString().split(''));
console.log(allCombinations);

How to guarantee sequential order with angular http rest api in for loop?

I'm trying to create a form that allows you to create multiple resources in sequential order.
Example below
Floor 1
Floor 2
Floor 3
...
Floor 9
The problem with the code is that the order is not guarantee.
My code below
let startAt = this.addAreasForm.controls['startAt'].value
const name = this.addAreasForm.controls['name'].value
const newArea = {name: name}
for (let i = 1; i < (amount + 1); i++) {
newArea.name = name + ' ' + startAt
startAt++
this.areasService.createArea(newArea, parentId)
.subscribe(
area => this.added.emit(area)
)
}
Can come back like
Floor 2
Floor 3
Floor 1
Floor 5
Floor 4
How do you handle async api calls to guarantee sequential order?
You can use async / await for that purpose with the Promise resolve:
for (let i = 1; i < (amount + 1); i++) {
await new Promise(resolve => {
newArea.name = name + ' ' + startAt
startAt++
this.areasService.createArea(newArea, parentId)
.subscribe(
area => {
this.added.emit(area);
resolve();
});
});
}
Remember to put async before your function. See this demo on StackBlitz.
You can try something like this, I don't exactly all your code from your services, but the main idea is this: In order to execute async code in order, you can build an array of promises and then to use Promise.all to take each result in the same order from the creation:
Promise.all
let startAt = this.addAreasForm.controls['startAt'].value;
const name = this.addAreasForm.controls['name'].value;
const newArea = {name: name};
Keep your services into variables I don't know from where your context comes.
const areasService = this.areasService,
added = this.added;
Make a function that create a promise for your subscribe:
function createAreaPromise(newArea, parentId) {
return new Promise((resolve, reject) => {
areasService.createArea(newArea, parentId)
.subscribe(area => resolve(area));
});
}
Than another function to build multiple an array of promises:
function buildPromises() {
let promises = [];
for (let i = 1; i < (amount + 1); i++) {
newArea.name = name + ' ' + startAt
startAt++
promises.push(createAreaPromise(newArea, parentId));
}
return promises;
}
Then solve them with Promise.all, to obtain the same order from creation
let promises = buildPromises();
Promise.all(promises)
.then(results => {
results.forEach(result => added.emit(result));
});
Here a live example:
function random() {
return Math.floor(Math.random() * 5);
}
function makePromise(index) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(index);
}, random() * 1000);
});
}
function buildPromises() {
let promises = [];
for(let i = 0; i < 5; i++) {
promises.push(makePromise(i));
}
return promises;
}
let promises = buildPromises();
Promise.all(promises)
.then(results => {
results.forEach(result => {
console.log(result);
});
});

Categories