processing items in a array with delay between each item in nodejs - node.js

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?

Related

foreach loop in sync function in nodejs

I have code written
function getDetails (req, res) {
const dbQuery = `call spGetSLAReportsDetails('${req.body.domainId}', ${req.body.days},'${req.body.type}','${req.body.app}')`
try {
connectDatabase(dbQuery).then((rows) => {
if (!_.isEmpty(rows.dbData) && !_.isEmpty(rows.dbData[0])) {
const resultList = []
rows.dbData.pop()
var bar = new Promise((resolve, reject) => {
rows.dbData[0].forEach((element, index, array) => {
let query = `select * from YCW.YWFWIC ic where ic.witem=${element.witem} and ic.reqno=${element.reqno};`
connectDatabase(query).then((data) =>{
for (var i = 0; i < data.dbData.length; i++) {
element[data.dbData[i]["cfield"]] = data.dbData[i]["cvalue"]
}
resultList.push(element)
// console.log(resultList)
}).catch((err) => {
console.log(err)
})
if (index === array.length -1) resolve();
});
});
bar.then(() => {
console.log(resultList);
});
res.status(msgCodeJson.ERR004.code).send({
result: resultList })
} else {
console.log("empty array")
res.status(msgCodeJson.ERR004.code).send({
message : "No data found"
})
// httpResponseHandlerError(res, msgCodeJson.ERR001.code, msgCodeJson.ERR001.msg)
}
}).catch(() => {
httpResponseHandlerError(res, msgCodeJson.ERR002.code, msgCodeJson.ERR002.msg)
})
} catch (err) {
httpResponseHandlerError(res, msgCodeJson.ERR009.code, msgCodeJson.ERR009.msg)
}
}
module.exports.getDetails = getDetails
i want data to be fit in resultlist but i get empty list after all operation.
while in foreach loop i am getting proper output.
kindly help in issue.
i tried with async foreach loop but some syntax error is coming.
kindly help
as mentioned in the comment of the code you're using
Best way to wait for .forEach() to complete
This is OK if there is no async processing inside the loop.
yet you have an async function inside your forEach callback, namly this:
connectDatabase(query).then((data) => {
for (var i = 0; i < data.dbData.length; i++) {
element[data.dbData[i]["cfield"]] = data.dbData[i]["cvalue"]
}
resultList.push(element)
}).catch((err) => {
console.log(err)
})
you'll need to resolve the "outer/parent" promise from inside the "inner/child" promise
I suggest using a regular good old for loop and/or checking the count of resolved promises against the rows.dbData[0].length and calling a final code/function once they match

Return true when detect event response

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

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>

Herarchy query using sequelize / nodejs

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.

Resources