Callback function getting called before initial function finishes - javascript

I am having issues with callback functions - I have a main function which has two callback functions inside. This is the main function
socket.on('play next video', function(data) {
removeVideo(cue[0], getCueFromDb(function() {
io.sockets.emit('next video');
}));
});
My removeVideo function looks like this:
function removeVideo(id, callback) {
Video.find({'id' : id}).remove(function(err, data) {
if (err)
console.log(err)
console.log("Removed video", id)
});
if (callback)
callback();
else
return
}
and the getCueFromDb function looks like this
function getCueFromDb(callback) {
Video.find({}).exec(function(err, videos) {
if (err) {
console.log(err)
}
if (videos.length) {
cue.length = 0 // empty array
videos.forEach(function(video) {
cue.push(video.id) // push all the videos from db into cue array
});
io.sockets.emit('send cue', {cue: cue});
}
else {
console.log("No more videos in database!")
}
if (callback)
callback();
else
return
});
}
However the functions aren't getting called in the correct order - am I doing something wrong?

Your callback needs to be inside your find.remove()
function removeVideo(id, callback) {
Video.find({'id' : id}).remove(function(err, data) {
if (err)
console.log(err)
console.log("Removed video", id)
if (callback)
callback();
else
return
});

You have to change removeVideo so callback we be called only after delete.
That's the code:
function removeVideo(id, callback) {
Video.find({'id' : id}).remove(function(err, data) {
if (err)
console.log(err)
console.log("Removed video", id)
if (callback)
callback();
else
return
}
});
So callback will be called only after vide is really removed.

Related

system - Sleep not working properly

I want to store all the data fetched from the database in arr_obj, then use this variable int async.forEachLimit function. For this reason i used async.series function, everything works fine except sleep(1000), code sleeps as soon as second function of async.series is called and then gives all the result together.Being a beginner in NodeJs i don't have much idea about all this.
var sleep = require('system-sleep');
//
//
var arr_obj = [];
async.series([
function (callback) {
Service.listAllUser(req.body, function (err, data) {
if(err) return callback(err);
arr_obj = data.toJSON();
callback();
});
},
function (callback1) {
console.log(arr_obj);
async.forEachLimit(arr_obj, 1, function (item, callback) {
Quality_Service.qualityService(item, function (err, data) {
if (err) return next(err);
console.log(data);
});
sleep(1000);
callback();
});
callback1();
}
], function (err) { //This function gets called after the two tasks have called their "task callbacks"
if (err) return next(err);
res.send("okay");
});
Try to use callback inside the foreach, with setTimeout
async.forEachLimit(arr_obj, function (item, callback) {
Quality_Service.qualityService(item, function (err, data) {
if (err) return next(err);
setTimeout(callback, 1000, err);
});
});

Mocha test are not running in right order using before/beforeEach

I use the following code and in the before I copy files that the tests after are using assertion but when I run it the before/each is happen after the test, what am I missing here?
I tried also with beforeach without success
describe(" Handler", function() {
before((done) => {
fs.stat(utils.pathSetup() + "/ins/", function (err, stats) {
if (!err) {
console.log("success");
} else {
let lclpath = utils.pathSetup();
Loder.add({folderPath: lclpath + "/test/testcontent/ins"});
console.log(err);
}
});
done();
});
//This are called before the previous statement why????????
//This should happen after the before has finished,they are in the same test block and this called is right after...
Handler.invoke(req, res)
.then((Ref) => {
}).done();
ee.on('Started', (arg) => {
evtResStart = arg;
});
in debug when I put BP it stops in before and click on step into take me to the Handler.invoke instead of inside the before...:(
Any idea what could be the reason for such thing?
If you are using done in your tests/befores, you need to call done() inside your callbacks, e.g.
before((done) => {
fs.stat(utils.pathSetup() + "/ins/", function (err, stats) {
if (!err) {
console.log("success");
} else {
let lclpath = utils.pathSetup();
Loder.add({folderPath: lclpath + "/test/testcontent/ins"});
console.log(err);
}
done();
});
});
Edit: Assuming the comments in your question are supposed to be formatted as code, is your Handler stuff in at it block?
it('should do something', () => {
Handler.invoke(req, res)
.then((Ref) => {
}).done();
ee.on('Started', (arg) => {
evtResStart = arg;
});
});
Have you tried putting done(); inside the callback of fs.stat?
In this way your tests should run as you want
before((done) => {
fs.stat(utils.pathSetup() + "/ins/", function (err, stats) {
if (!err) {
console.log("success");
} else {
let lclpath = utils.pathSetup();
Loder.add({folderPath: lclpath + "/test/testcontent/ins"});
console.log(err);
}
done();
});
});

async.js return value form last function

I would like create find method in my User object. This function should returns user. But for this example it returns only the text.
But I don't know how return value for waterfall.
When I run
console.log(User.find("575578a9f95d6de1354327ef"));
I got 'undefined' in my output, but I except 'function find shoud return this value', What I should to do if I want get 'function find shoud return this value' text on my output
User = {
collectionName: 'users',
find: function(id){
async.waterfall(
[
function(callback) {
MongoClient.connect('mongodb://127.0.0.1:27017/lingogo', function(err,db) {
if(err) { throw err}
callback(null, db, id);
});
},
function(db,id, callback) {
var collection = db.collection(User.collectionName);
collection.find({'_id': ObjectID(id)}).toArray(function (err, result) {
if (err) { throw err };
if (result[0] && result[0]._id != '') {
return callback(null,result[0]);
}
return callback(null,null);
})
},
],
function (err, user) {
return 'function find shoud return this value';
}
);
}
}
console.log(User.find("575578a9f95d6de1354327ef"));
Function find must have a callback too, that you call in a callback of waterfall. You cannot return a value synchronously from an asynchronous function.
find: function (id, callback) {
async.waterfall(..., function (...) {
callback(null, return_value);
});
}
That should be called like
User.find("575578a9f95d6de1354327ef", function (err, return_value) {
console.log(return_value);
});

Calling a parameterized callback function within a mongoose async callback function becomes 'undefined'

I am having a weird problem with calling a callback inside another callback from mongoose.
Setup : MEAN Stack.
myFunc = function (cb) {
var projection = {
'_id': 0,
'var1': 1,
'var2': 1
}
var order = {
'var1': 1
}
User.find({})
.select(projection).sort(order)
.exec(function(err, docs){
if(err){
console.log(err);
cb(err,docs);
} else {
console.log(docs);
cb(err,docs);
}
});
};
going to the lines where cb(err,docs) will result in
"ReferenceError: cb is not defined"
the weird part is
I have functions with even deeper nested callbacks that can invoke the "cb" normaly.
myFunc = function(cb){
model1.count({var1:'test'}, function (err, count) {
if(count) {
model2.findOne({dat1:'hoho'}, function (err, doc){
if (err) {
console.error(err);
cb(err,doc);
} else {
cb(err,doc);
}
});
} else {
cb({message: "No items found"}, null);
}
})
}
The code above will be invoked like so...
function init(something){
myfunc(function(err, doc) {
if (err){
console.log(err.message);
} else {
//do something about doc
}
});
}
ugh, it seems that the calling function did not properly follow the rules.
it called
myFunc(json, function(err,doc){
//do something
})
wrong param count...

Retry an async function if the error is retryable

How can I change my logic to retry if the err.retryable = true in the following code:
async.each(queues, function (queue, callback) {
sqs.getQueueUrl({'QueueName': queue.queue}, function (err, qurl) {
if (err) {
if (err.retryable) {
// How to retry sqs.getQueueUrl({'QueueName': queue.queue}...?
} else {
console.error(err, err.stack);
callback(err);
}
}else{
//Do lots of things here
}
})
}, function (err) {
//...
})
In addition to the advice by dfsq to name your callback and use it an asynchronously recursive manner, see also async.retry from the async module by Caolan McMahon. Example:
async.retry(3, apiMethod, function(err, result) {
// do something with the result
});
More complex example:
async.auto(
{
users: api.getUsers.bind(api),
payments: async.retry(3, api.getPayments.bind(api))
}, function(err, results) {
// do something with the results
}
);
More details in the docs.
UPDATE
A better solution for your use case:
I wrote a utility function that you can use to make your original method support any number of retries (with err.retryable support).
You can use it this way:
var retryingFunction = withRetries(sqs, sqs.getQueueUrl);
(Note that you need to provide both sqs and sqs.getQueueUrl)
And now you can use the retryingFunction just like you would use sqs.getQueueUrl but with a number of retries as the first arguments. The retries will only be done when err.retryable is true.
So now, instead of:
sqs.getQueueUrl({'QueueName': queue.queue}, function (err, qurl) {
// ...
});
you can use:
retryingFunction(3, {'QueueName': queue.queue}, function (err, qurl) {
// ...
});
where 3 is the number of retries.
And this is the function that I wrote to make the above possible:
function withRetries(obj, method) {
if (!method) {
method = obj;
obj = null;
}
if (typeof method != "function") {
throw "Bad arguments to function withRetries";
}
var retFunc = function() {
var args = Array.prototype.slice.call(arguments);
var retries = args.shift();
var callback = args.pop();
if (typeof retries != "number" || typeof callback != "function") {
throw "Bad arguments to function returned by withRetries";
}
var retryCallback = function (err, result) {
if (err && err.retryable && retries > 0) {
retries--;
method.apply(obj, args);
} else {
callback(err, result);
}
};
args.push(retryCallback);
method.apply(obj, args);
};
return retFunc;
}
See this LIVE DEMO to play with it and see how it works.
It works fine in the demo, I hope it will also work for your code.
You can give queue callback a name and provide it in retry request again. Try this:
async.each(queues, function (queue, callback) {
sqs.getQueueUrl({'QueueName': queue.queue}, function queueCallback(err, qurl) {
if (err) {
if (err.retryable) {
sqs.getQueueUrl({'QueueName': queue.queue}, queueCallback);
} else {
console.error(err, err.stack);
callback(err);
}
} else {
//Do lots of things here
}
});
}, function (err) {
//...
});

Categories