Okay so i know that it is bad pratice to force node.js to be syncronous but in this case i have no choice.
I am trying to create a tree like structure of my categories for this i have created this function:
router.route('/api/categoryStructure')
.get(function (req, res) {
var cat = Category.build();
cat.getRootCategories(function (categories) {
var result = [];
var root = categories;
root.forEach(function (y) {
var tmp = findTree(y);
result.push(tmp);
});
})
});
function findTree(rootCategory) {
var root = [rootCategory];
var result = [];
(function loop() {
var element = root[0];
var category = Category.build();
category.retrieveByParentId(element.id, function (categories) {
if (categories) {
element.dataValues.subcategories = categories;
categories.forEach(function (division) {
root.push(division);
});
root.splice(0, 1);
if (result.length == 0) {
result.push(element);
loop()
}
else if (root.length == 0) {
return result;
}
else {
loop()
}
}
else
{
result = root;
return result;
}
});
}());
}
Now as you can see it loop through each of the root categories to find all subcategories and all of their subcategories.
This works perfectly fine however there is a problem
The tmp variable in my loop is set to undefined because of the asyncronous behavior of node. This means that my array is being filled up with undefined/ null values.
So my question is how can i avoid this?
First solution:
Lets add some logic to findTree to make it accepts callbacks
function findTree(rootCategory, callback) {
var root = [rootCategory];
var result = [];
(function loop() {
var element = root[0];
var category = Category.build();
category.retrieveByParentId(element.id, function (categories) {
if (categories) {
element.dataValues.subcategories = categories;
categories.forEach(function (division) {
root.push(division);
});
root.splice(0, 1);
if (result.length == 0) {
result.push(element);
loop()
}
else if (root.length == 0) {
callback(result);
}
else {
loop()
}
}
else
{
result = root;
callback(result);
}
});
}());
}
then you can now call findTree with a callback having any logic you want to be executed secondly.
findTree(y,function(data){
result.push(data);
});
Another way using async module.
You could use async module . Its auto function is awesome . If you have function A() and function B() and function C() . Both function B() and C() depend of function A() that is using value return from function A() . using async module function you could make sure that function B and C will execute only when function A execution is completed .
Ref : https://github.com/caolan/async
async.auto({
A: functionA(){//code here },
B: ['A',functionB(){//code here }],
C: ['A',functionC(){//code here }],
D: [ 'B','C',functionD(){//code here }]
}, function (err, results) {
//results is an array that contains the results of all the function defined and executed by async module
// if there is an error executing any of the function defined in the async then error will be sent to err and as soon as err will be produced execution of other function will be terminated
}
})
});
Related
Here is what should be output to console:
1 hello
2 a
3 b
And here is the code for which i should make a class or a function:
var d = new deferred();
d.then(function(res) {
console.log("1 ", res);
var d1 = new deferred();
setTimeout(function() {
d1.resolve("a");
}, 150);
return d1;
});
d.then(function(res) {
console.log("2 ", res);
return "b";
});
d.then(function(res) {
console.log("3 ", res);
return "c";
});
d.resolve("hello");
I should create a class or a
function with name "deferred"
I've already done almost everything except i can't make it get the result from setTimeout
function.
function deferred() {
this.arrfunc = [];
this.buffstr = null;
this.bufffunc;
this.result;
this.then = function(callback) {
this.arrfunc.push(callback);
}
this.wait = function() {
while (this.buffstr == null) {}
return this.buffstr;
}
this.resolve = function(str) {
this.buffstr = str;
while (this.arrfunc.length != 0) {
//console.log(typeof(this.buffstr));
if (typeof(this.buffstr) === "object") {
this.buffstr = this.buffstr.wait();
}
this.bufffunc = this.arrfunc.shift();
this.buffstr = this.bufffunc(this.buffstr);
}
}
}
The main problem in my implemenetation that its somehow stuck in while loop. And don't want to get a result after setTimeout expired.
It seems like maybe you are making this too complicated with all the different state properties. If these are you only requirements, you really only need a queue of callbacks to hold on to all the functions passed into then(). Like regular promises you need to act differently depending on whether the callback returns a regular value or another deferred instance.
function deferred() {
this.callbacks = []
}
deferred.prototype.then = function(cb) {
this.callbacks.push(cb)
}
deferred.prototype.resolve = function(val) {
let f = this.callbacks.shift()
if (f) {
let n = f(val)
if (n instanceof deferred) n.then((v) => this.resolve(v))
else this.resolve(n)
}
}
var d = new deferred("one");
d.then(function(res) {
console.log("1 ", res);
var d1 = new deferred("two");
setTimeout(function() {
d1.resolve("a");
}, 550);
return d1;
});
d.then(function(res) {
console.log("2 ", res);
return "b";
});
d.then(function(res) {
console.log("3 ", res);
return "c";
});
d.resolve("hello")
My question is related to following code -
Scenario 1:
var queue = [];
var busy = false;
exports.getAProfile = function (params, cb) {
queue.push({params: params, cb: cb});
if (!busy) {
checkQueue(function (c, next) {
service.getAProfileNow(c.params, function (err, res) {
c.cb(err,res);
next();
});
});
}
}
function checkQueue(actualTask) {
var c = queue.shift();
if (c) {
busy = true;
actualTask(c, function () {
checkQueue(actualTask);
})
return queue.length;
} else {
busy = false;
return queue.length;
}
}
Scenario 2:
var queue = [];
var busy = false;
var actualTask = function (c, next) {
service.getAProfileNow(c.params, function (err, res) {
c.cb(err,res);
next();
});
}
exports.getAProfile = function (params, cb) {
queue.push({params: params, cb: cb});
if (!busy) {
checkQueue();
}
}
function checkQueue() {
var c = queue.shift();
if (c) {
busy = true;
actualTask(c, function () {
checkQueue(actualTask);
})
return queue.length;
} else {
busy = false;
return queue.length;
}
}
Difference in both codes are -
The function actualTask is (i guess) defined many times in first scenario while in Second scenario it is defined only once.
is 'what I interpret' correct? then which one is better? and why? is performance of code different in both?
Note: service module is require-d in code, it only queries database.
Defining function just once does not make the performance or execution fast. It will just reduce the file size of your code and make your code formation pretty well. In both cases, the function must have to jump to procedure call.
How do I iterate over a asynchronous function in nodejs stopping depending on callback return?
Example:
suppose I have following function
var fn1 = function(i, callback){
// code here
callfunction(i, function(err, p2){
if(err) throw err;
if(something) return callback(true);
else return callback(false);
});
}
I need to iterate and call for i=1,2,3,... until my callback returns false.
I tried using async whilst, but I do not think it can helps me, because the test function also needs callback, and the function for test and iteratee are same, I cannot call twice.
If I understand your problem correctly. What about some recursion
const maxTries = 10;
// just a stupid counter to run a be able to change return value
let numberOfCalls = 0;
const asyncCall = () => {
if (numberOfCalls === 9) {
return Promise.resolve(false);
}
numberOfCalls += 1;
return Promise.resolve();
};
function iterate(calls = 0) {
if (maxTries === calls) {
return Promise.resolve();
}
return asyncCall().then(value => {
if (value === false) {
return value;
}
return iterate(calls + 1);
});
}
iterate().then(result => {
if (result === undefined) {
//to many tries
}
console.log('result', result);
});
I'm trying to return the output of the randomizer function within a route... I keep getting 'undefined' - but no idea what I'm doing wrong...
var randomizer = function() {
// A load of stuff happens here.. and functions that are needed by the pullOut function (I've removed for brevity)
var pullOut = function(pick) {
if (playerList.length !== pick) {
var random_item = getRandomItem(list, weight);
if (playerList.indexOf(random_item) == -1) { // doesn't exist. So add to array.
playerList.push(random_item);
}
pullOut(pick);
} else {
console.log(playerList)
return playerList;
}
}
return pullOut(pick);
}
router.route('/ordercreated')
.post(function(req, res) {
var orderedItems = req.body.line_items;
// I foreach through all the items - calling the randomizer function on each one...
_.forEach(orderedItems, function(n) {
Pack.findOne({
'product_id': n.variant_id
}, function(err, pack) {
if (err) {
return res.send(err);
}
if (pack) {
var list = [];
var weight = [];
_.forEach(pack.playerData, function(n) {
list.push(n.name);
weight.push(parseInt(n.chance));
});
console.log('randomizing', randomizer(pack.title, list, weight, n.qty, pack.pick));
}
});
});
res.sendStatus(200);
})
Your "pullOut" function calls itself, but it throws away the result of that call.
var randomizer = function() {
// A load of stuff happens here.. and functions that are needed by the
// pullOut function (I've removed for brevity)
var pullOut = function(pick) {
if (playerList.length !== pick) {
var random_item = getRandomItem(list, weight);
if (playerList.indexOf(random_item) == -1) { // doesn't exist. So add to array.
playerList.push(random_item);
}
return pullOut(pick); // <--- add return
} else {
console.log(playerList)
return playerList;
}
}
return pullOut(pick);
}
Without that return, when the function took that path through the main if statement it would return undefined.
As I understand it the 100 asyncFunctions in the code below will not be executed until after func has returned true, and at this point the referens to i will not be valid. Nothing in this code would work as expected I guess.
Psedo example code:
function func(){
var needToKnow = []
for i = 1 to 100 {
needToKnow[i] = asyncFunction( i )
}
//Do some work on needToKnow[i]
return true
}
What would be the Javascript way to do something like this?
Use callbacks:
function func(callback) {
var needToKnow = [],
max = 100;
for (var i = 0; i < max; i++) {
asyncFunction(i, function (result) {
needToKnow.push(result);
if (needToKnow.length == max) { // or something that let you know that its finished
callback(needToKnow);
}
});
}
}
function asyncFunction(i, callback) {
setTimeout(function () {
callback({ index: i });
}, 1000); // Im an async func!
}
And use it this way:
func(function (result) {
console.log(result);
});
Be careful, don't get in callback hell
Here is an example using the Q Promise library:
function functionThatCouldThrowError(i){
//It doesn't, but just to give an idea of error propagation.
return { index: i };
}
function asyncFunction(i) {
var deferred = Q.defer();
setTimeout(function () {
try{
var data = functionThatCouldThrowError(i);
deferred.resolve(data);
} catch (error) {
deferred.reject({ index: i, error: error });
}
}, 1000);
return deferred.promise;
}
function doAll() {
var needToKnow = []
for (var i = 0; i < 100; i++) {
needToKnow[i] = asyncFunction( i );
}
return Q.all(needToKnow);
}
doAll().then(function(arg) {
//arg contains all 100 elements
alert("All done");
})
Update: expanded the example to demonstrate how to handle errors.
Plunker:http://plnkr.co/edit/djWpTKxgvzK2HmkVwvTy?p=preview