Loop synchronous multiple async.whilst - node.js

I want to use many included loops in node.js in synchronous mode.
Example :
for (var i = 0; i < length1; i++) {
for (var j = 0; j < length2; j++) {
for (var k = 0; k < length3; k++) {
//completed 3
}
//completed 2
}
//do completed 1
}
How to do this with async? I tried this :
exports.myFunction = function (callback) {
var finalListA = new Array();
var pos = 0;
Model_A.find().populate('listOfItems')
.lean().exec(function (err, As) {
if (err) {
console.log(err);
return callback(err, null);
} else {
//For each A
var i = 0;
async.whilst(
function () {
return i < As.length;
},
function (callback1) {
var isActive = false;
//For each B into the A
var j = 0;
async.whilst(
function () {
return j < As[i].Bs.length;
},
function (callback2) {
Model_B.findById(AS[i].Bs[j]._id, function (err, B) {
if (err) {} else {
var k = 0;
// For each C in the B
async.whilst(
function () {
return k < B.Cs.length;
},
function (callback3) {
if (B.Cs[k].dateEnd >= Date.now()) {
isActive = true;
}
k++;
callback3();
},
function (err) {
console.log("3 COMPLETED");
}
);
}
});
j++;
callback2();
},
function (err) {
console.log("2 COMPLETED");
if (err) {} else {
if (isActive == true) {
finalListA[pos] = As[i];
pos = pos + 1;
}
}
}
);
i++;
callback1();
},
function (err) {
console.log("1 COMPLETED");
if (err) {} else {
return callback(null, finalListA);
}
}
);
}
});
}
The trace shows me :
COMPLETED 2
COMPLETED 2
COMPLETED 1
COMPLETED 3
COMPLETED 3
The order expected is :
COMPLETED 3
COMPLETED 3
COMPLETED 2
COMPLETED 2
COMPLETED 1

You must call the callbacks of the higher loops from the end callback of your whilst loop (like you did with the outermost callback), instead of calling them synchronously from the whilst body in which you just started the next level iteration.
Btw, I don't know what you actually want to do, but whilst does not seem the best choice for iterating arrays. Use the parallel each or the serial eachSeries (or their map or reduce equivalents).

I've recently created simpler abstraction called wait.for to call async functions in sync mode (based on Fibers). It's at an early stage but works. It is at:
https://github.com/luciotato/waitfor
Using wait.for, you can call any standard nodejs async function, as if it were a sync function.
I do not understand exactly what are you trying to do in your code. Maybe you can explain your code a little more, or give some data example.
I dont' know what Model_A or Model_B are... I'm guessing most of your code, but...
using wait.for your code migth be:
var wait=require('wait.for');
exports.myFunction = function(callback) {
//launchs a Fiber
wait.launchFiber(inAFiber,callback);
}
function inAFiber(callback) {
var finalListA = new Array();
var pos = 0;
var x= Model_A.find().populate('listOfItems').lean();
As = wait.forMethod(x,"exec");
//For each A
for(var i=0;i<As.length;i++){
var isActive = false;
//For each B into the A
for(var j=0; j < As[i].Bs.length;j++){
var B=wait.forMethod(Model_B,"findById",AS[i].Bs[j]._id);
// For each C in the B
for(var k=0; k < B.Cs.length;k++){
if(B.Cs[k].dateEnd >= Date.now()) {
isActive = true;
}
}
console.log("3 COMPLETED");
}
console.log("2 COMPLETED");
if(isActive == true) {
finalListA[pos] = As[i];
pos = pos + 1;
}
};
console.log("1 COMPLETED");
return callback(null,finalListA);
}
Also, for what I see, you should break the loops as soon as you find one item (isActive), and you don't need the var pos. Doing that your code will be:
var wait=require('wait.for');
exports.myFunction = function(callback) {
//launchs a Fiber
wait.launchFiber(inAFiber,callback);
}
function inAFiber(callback) {
var finalListA = [];
var x= Model_A.find().populate('listOfItems').lean();
As = wait.forMethod(x,"exec");
var isActive;
//For each A
for(var i=0;i<As.length;i++){
isActive = false;
//For each B into the A
for(var j=0; j < As[i].Bs.length;j++){
var B=wait.forMethod(Model_B,"findById",AS[i].Bs[j]._id);
// For each C in the B
for(var k=0; k < B.Cs.length;k++){
if(B.Cs[k].dateEnd >= Date.now()) {
isActive = true;
break;//for each C
}
} //loop for each C
console.log("3 COMPLETED");
if (isActive) break;//for each B
} //loop for each B
if (isActive) finalListA.push(As[i]);
console.log("2 COMPLETED");
} //loop for each A
console.log("1 COMPLETED");
return callback(null,finalListA);
}

Related

How to get code to execute in order in node.js

I am trying to finish my script, but for some reason i don't know, it refuses to execute in the order i put it in.
I've tried placing a 'wait' function between the JoinRequest update function and the following code, but when run, it acts as if the function call and wait function were the other way round, countering the point of the wait().
const Roblox = require('noblox.js')
var fs = require('fs');
var joinRequests = []
...
function wait(ms) {
var d = new Date();
var d2 = null;
do { d2 = new Date(); }
while(d2-d < ms*1000);
};
...
function updateJReqs() {
Roblox.getJoinRequests(4745601).then((array) => {
var i;
var final = [];
for(i = 0; i < array.length; i++) {
final.push(array[i].username);
};
if(final === '') {
final = '-None';
};
joinRequests = final
console.log('Updated join requests.')
});
}
function check() {
setTimeout(() => {
fs.readFile('Request.txt',encoding = 'utf-8', function(err, data) {
if (err) {
check();
} else {
updateJReqs(); //for some reason this function is executed alongside the below, not before it.
// Tried putting wait(x) in here.
console.log('Request received: ' + data)
var solution = joinRequests
console.log('Fuffiling request with ' + solution)
fufillRequest(solution)
fs.unlink('Request.txt', function(err) {
if(err) throw err;
});
check();
}
});
}, 400)
}
check();
The script is supposed to wait until a file is created (accomplished), update the list of join requests (accomplished) and then create a new file with the list of join requests in(not accomplished).
if I understand your code you work with async code, you need to return a promise in updateJReqs and add a condition of leaving from the function because you have an infinite recursion
function updateJReqs() {
return new Promise(resolve => {
Roblox.getJoinRequests(4745601).then((array) => {
var i;
var final = [];
for(i = 0; i < array.length; i++) {
final.push(array[i].username);
};
if(final === '') {
final = '-None';
};
joinRequests = final
console.log('Updated join requests.')
resolve();
});
}
}
async function check() {
setTimeout(() => {
fs.readFile('Request.txt',encoding = 'utf-8', function(err, data) {
if (err) {
await check();
} else {
await updateJReqs();
// Tried putting wait(x) in here.
console.log('Request received: ' + data)
var solution = joinRequests
console.log('Fuffiling request with ' + solution)
fufillRequest(solution)
fs.unlink('Request.txt', function(err) {
if(err) throw err;
});
// you dont have an exit from your function check();
return 'Success';
}
});
}, 400)
}
check().then(res => console.log(res));

how to call function in node loop one at a time

this block of code process else block first and then if block, for all request at once, i need a code that execute 5 iterations stop for 15 sec. and then again continue from next 5 iterations and so on.
function passotken(token, callback) {
async.waterfall([
function(callback) {
db.executesql("select top 20 ext_id as EMPNUM , data as datae from newtable", function (data, err) {
callback(null, data);
});
},
function(data, callback) {
var _json_parse = JSON.parse(JSON.stringify(data));
var rows = data.length;
console.log(rows)
var cnt = 1;
for (var row = 1; row <= rows; row++) {
logger.info(_json_parse[row-1].EMPNUM);
//console.log(dateFormat(_json_parse[row].datae));
var req = 'https://pratik.com/ta/rest/v2/companies/|RHV/employees/|' +_json_parse[row-1].EMPNUM + '/timesheets?date=' + dateFormat(_json_parse[row-1].datae, "isoDate");
//console.log(req);
var myXMLText = req;
reques.push(myXMLText);
}
// console.log(reques);
for (var a = 0; a < rows; a++) {
//CURRENTLY PROCESSING ALL REQUEST IN IF BLOCK AND STOP FOR 15 SEC FOR ONLY FIRST IF CONDITION AFTER THAT ALL IF CONDITION PROCESSING WITHOUT HALT
if(a%5==0)
{
console.log("if");
//console.log(reques[a]);
//postreq(reques[a],token,sleeped(a));
/* setTimeout(function(){sleeped(reques[a],token);;
},15000); */
sleeped(reques[a],token);
//SHOULD PROCESS IF BLOCK ONCE AND STOP FOR 15 SEC
}
else
{
postreqELSE(reques[a],token);
//SHOULD PROCESS ALL REQUECT IN ELSE BLOCK TILL IF CONDITION ABOVE NOT SATISY
}
}
Promise.all(ps)
.then((results) => {
console.log("results"); // Result of all resolve as an array
}).catch(err => console.log("err"));
},
], function(err, result) {
if (!err)
console.log("Successfully completed")
else console.log(err);
});
};
function callback() {
console.log("completed successfully");
}
function postreq(request1,token)
{
//BLOCK
}
function sleeped(requesarr,token)
{
console.log("in sleeping");
//console.log(requesarr,token)
setTimeout(function(){
postreq(requesarr,token);
},15000);}
function postreqELSE(request2,token1)
{
console.log("in 3RD function");
//BLOCK2
}
Sure, this question was answered previously here: What is the JavaScript version of sleep()?
Following is an example which will do what you're requesting:
var reques = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function checkLoop()
{
for (var a = 0; a < 20; a++)
{ console.log("["+a+"] a%5 is: "+a%5);
console.log ( new Date().toLocaleTimeString());
if(a%5==0)
{ console.log("if");
console.log ( new Date().toLocaleTimeString());
await sleep(15000);
}
else
{ console.log("else");
console.log ( new Date().toLocaleTimeString());
}
}
}
checkLoop();
This prints out the following: (partial) Important to note that it will always pause on the first iteration because the index starts at 0, not 1.
[0] a%5 is: 0
8:58:38 AM
if
[1] a%5 is: 1
8:58:53 AM
else
8:58:53 AM
[2] a%5 is: 2
8:58:53 AM
else
8:58:53 AM
[3] a%5 is: 3
8:58:53 AM
else
8:58:53 AM
[4] a%5 is: 4
8:58:53 AM
else
8:58:53 AM
[5] a%5 is: 0
8:58:53 AM
if
8:58:53 AM
[6] a%5 is: 1
8:59:08 AM
else
8:59:08 AM
[7] a%5 is: 2
8:59:08 AM
else
8:59:08 AM
If you can't use async, then try this approach: (interval set at 1.5 secs for easier testing. just change the 1500 to 15000 to = 15 seconds)
var reques = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
var _interval;
var timeToWait = 1500;
function checkLoop(_init)
{
clearInterval(_interval);
console.log("checkLoop entered with value of: ", _init);
var start = (typeof(_init) == "undefined") ? 0 : _init;
console.log("start is: "+start);
for (var a = start; a < 20; a++)
{ console.log("["+a+"] a%5 is: "+a%5);
console.log ( new Date().toLocaleTimeString());
if(a%5==0)
{ console.log("if");
console.log ( new Date().toLocaleTimeString());
(function(_idx){_interval = setInterval(function(){checkLoop(_idx+1)}, timeToWait);})(a)
break;
}
else
{ console.log("else");
console.log ( new Date().toLocaleTimeString());
}
}
}
checkLoop();

Redis in Nodejs for loop not working properly

I have a for loop like below which isn't getting executed as expected.
var redis = require('redis');
var client = redis.createClient();
var arr = [{title:"title1"},{title:"title2"},{title:"title3"},{title:"title4"}];
for(var i =0; i<arr.length; i++){
//console.log(arr[i]);
var obj1 = arr[i];
client.get(obj1.title, function(err, response){
if(err){
console.log(err);
}
if(response){
if(i%3==0){
client.del(obj1.title);
}else{
client.incr(obj1.title);
}
}else{
client.set(obj1.title, 1);
}
});
}
The output on running the below code afterwards was
for(var i=0; i<arr.length; i++){
client.get(arr[i].title, redis.print);
}
The output:
Reply: null
Reply: null
Reply: null
Reply: null
Reply: null
Reply: null
Reply: 2
which was not what i expected, since all values except the one divisible by 3 should be atleast 1;
Please create a new function. In the new function, you can delete, increment or creating the new key.
The below code works fine for me. Please check.
var redis = require('redis');
var client = redis.createClient();
var arr = [ {
title : "title1"
}, {
title : "title2"
}, {
title : "title3"
}, {
title : "title4"
} ];
function delOrIncr(obj1, i) {
client.get(obj1.title, function(err, response) {
if (err) {
console.log(err);
}
if (response) {
if (i % 3 === 0) {
console.log('Deleting >' + obj1.title);
client.del(obj1.title);
} else {
console.log('Increment >' + obj1.title);
client.incr(obj1.title);
}
} else {
console.log('Creating new >' + obj1.title);
client.set(obj1.title, 1);
}
});
}
for (var i = 0; i < arr.length; i++) {
delOrIncr(arr[i], i);
}
Note:-
Please run the get as a separate program to check the result of the above program.
var redis = require('redis');
var client = redis.createClient();
var arr = [{title:"title1"},{title:"title2"},{title:"title3"},{title:"title4"}];
for(var i =0; i<arr.length; i++){
//console.log(arr[i]); // this is cool
var obj1 = arr[i];
client.get(obj1.title, function(err, response){
if(err){
console.log(err);
}
if(response){
if(i%3==0){
// mistake 1:
// due to async op,loop will already be over and i will be 3 here
// mistake 2:
// obj1 will be arr[3] here, not what you were expecting :D
client.del(obj1.title);
}else{
client.incr(obj1.title);
}`enter code here`
}else{
// so only the last obj gets stored.
client.set(obj1.title, 1);
}
});
}

DocumentDB, How to work with continuationToken in a SP

The next SP suppose to run over the collection and keep query for the next batch of documents (10 docs every batch). but instead return the same 10 documents every time.
function sample(prefix) {
var continuations = [],
ids = [],
context = getContext(),
collection = context.getCollection(),
response = context.getResponse();
var queryOptions = { pageSize: 10, continuation: null };
for (i = 0; i < 10; i++) {
// get all user wish list actions
var query = "select * from w",
accept = collection.queryDocuments(collection.getSelfLink(), query, queryOptions, processMultiUsers);
if (!accept) throw "Unable to read user's sessions";
}
getContext().getResponse().setBody(ids);
function processMultiUsers(err, docs, options) {
if (err) throw new Error("Error: " + err.message);
if (docs == undefined || docs.length == 0) throw new Error("Warning: Users not exists");
for (j = 0; j < docs.length; j++) {
ids.push(docs[j].UserId);
}
queryOptions.continuation = options.continuation;
continuations.push(options.continuation);
}}
In the script that you wrote, the execution of the queries are done synchronously and they are queued up with the same initial continuation token, which is null. Instead, we need to take the token from the first query and then queue the next and continue.
The below sample should help achieve what you are looking for
function sample(continuationToken) {
var collection = getContext().getCollection();
var maxResult = 10;
var documentsProcessed = 0;
var ids = [];
var filterQuery = "select * from w";
tryQuery(continuationToken);
function tryQuery(nextContinuationToken) {
var responseOptions = { continuation: nextContinuationToken, pageSize: maxResult };
if (documentsProcessed >= maxResult || !query(responseOptions)) {
setBody(nextContinuationToken);
}
}
function query(responseOptions) {
return (filterQuery && filterQuery.length) ?
collection.queryDocuments(collection.getSelfLink(), filterQuery, responseOptions, onReadDocuments) :
collection.readDocuments(collection.getSelfLink(), responseOptions, onReadDocuments);
}
function onReadDocuments(err, docFeed, responseOptions) {
if (err) {
throw 'Error while reading document: ' + err;
}
documentsProcessed += docFeed.length;
for (var i = 0; i < documentsProcessed; i++) {
ids.push(docFeed[i].UserId);
}
if (responseOptions.continuation) {
tryQuery(responseOptions.continuation);
} else {
setBody(null);
}
}
function setBody(continuationToken) {
var body = { continuationToken: continuationToken, documentsProcessed: documentsProcessed, ids: ids };
getContext().getResponse().setBody(body);
}
}

NodeJS continues executing before object finished initializing

I'm creating an object in node like this:
obj.push(new MyObject());
here is my object code
function MyObject() {
this.arr= [];
for (x= 0; x< 2; x++) {
this.arr.push([]);
for (y= 0; y< 400; y++) {
this.arr[x].push([]);
for (z= 0; z< 1008; z++) {
this.arr[x][y].push(0);
}
}
}
}
after I call obj.push, program execution continues and the operations at index 1006, 1007... don't work because I suspect the array isn't finished initializing. How can I make sure that the array is initialized before program execution continues?
edit:
for (i = 0; i < 1; i++) {
(function(i) {
asyncTasks.push(function(callback) {
obj.push(new MyObject());
some_class.bigOperation(obj[i], function() {
callback();
});
});
})(i);
}
async.parallel(asyncTasks, function() {
console.log("finished initializing");
});
Not quite sure what you mean by array isn't initialized. You're creating your objects in parallel, which depending on your actual implementation might be the problem. Take a look at this version and see if it helps.
var async = require('async');
var asyncTasks = [];
var obj = [];
function bigOperation(myObj, done) {
setTimeout(function() {
console.log('done with long operation');
done();
}, 1000);
}
function MyObject() {
this.arr = [];
for (x= 0; x< 2; x++) {
var xlayer = [];
this.arr.push(xlayer);
for (y= 0; y< 400; y++) {
var ylayer = [];
xlayer.push(ylayer);
for (z= 0; z< 1008; z++) {
ylayer.push(0);
}
}
}
console.log('obj created');
}
for (i = 0; i < 10; i++) {
var myObj = new MyObject();
obj.push(myObj);
(function(myObj) {
asyncTasks.push(function(callback) {
bigOperation(myObj, callback);
});
})(myObj);
}
console.log('starting async tasks');
async.parallel(asyncTasks, function() {
console.log("finished initializing");
});
use callback, node is async, code is executing line by line but will not wait
initialize = function(callback){
var a = [];
//do your initialization here
callback(a);
}
initialize(function(data){
// array initialized you can now populate it.
});
However, you loop is a bit weird to me. Are you sure you it is correct ?

Resources