callback was already called! in loopback, in updateAll function - javascript

I'm using the loopback, here while making the update call with list of objects in array.
I get in the callback is already called!
The scene is, I have defined the callback inside the loop, and in the first loop, it is get in called actually.
I am looking for the way where
I should update all list of object in query MySQL plan call.
Inward.updateIsActiveDetails = function(data, callback) {
var id = _.map(data, 'id');
if (id.length > 0) {
_.forEach(id, id => {
console.log('id....:', id)
Inward.updateAll({id}, {
isActive: 0,
}).then(updateresult => {
console.log(updateresult);
// callback(error); showing err with it... (callback already called)
}).catch(function(error) {
callback(error);
});
});
} else {
callback(null, {
success: true,
msg: 'No records to update',
});
}
};
output:
id....: 3
id....: 4
{ count: 1 }
{ count: 1 }
appreciate for right solution

The callback is supposed to be called once, you're calling it in the loop, so it will be called for each iteration of the loop. More than once. The following would be correct if for whatever reason you can't use async/await.
Inward.updateIsActiveDetails = function(data, callback) {
var id = _.map(data, 'id');
var len = id.length;
var resultList = [];
// When you call this function we add the results to our list
// If the list of updates is equal to the number of updates we had to perform, call the callback.
function updateResultList(updateResult) {
resultList.push(updateResult);
if (resultList.length === len) callback(resultList);
}
if (len > 0) {
_.forEach(id, id => {
Inward.updateAll({id}, {
isActive: 0,
})
.then(updateResult);
});
} else {
callback(null, {
success: true,
msg: 'No records to update',
});
}
};
With async/await it would be much shorter.
Inward.updateIsActiveDetails = async function(data) {
const results = [];
for(let i = 0; i < data.length; i++) {
results.push(await Inward.updateById(data[i].id));
}
return results;
}

Here is my final and working answer.
Basically, updateAll query runs once and it will run as an inbuilt query
id: {
inq: _.map(data, 'id'),
}
So, after running that it will update the respective row only! very interesting.
Inward.updateIsActiveDetails = function (data, callback) {
Inward.updateAll({
id: {
inq: _.map(data, 'id'),
},
}, {
isActive: 0,
}, function (error, resultDetails) {
if (error) {
console.log('error', error);
callback(error);
} else {
console.log('resultDetails', resultDetails);
callback(null, resultDetails);
}
});
};

Related

How to do a callback in nodeJS

I'm working on a nodeJS script and I would like to know how to execute a function after an another one.
Because actually i need to save in my database some data and then retrieve them. However for the moment my retrieve is executed before my save :/
Have already looked on internet there is a lot of example I tried them but for the moment no one worked ... I should probably do something wrong if somebody could help me on it :)
function persistMAP(jsonData, callback) {
console.log(jsonData);
//Deck persistance
for (var i = 0; i < 1; i++) {
(function(i) {
var rowData = new DeckDatabase({
_id: new mongoose.Types.ObjectId(),
DeckNumber: Number(jsonData.Deck[i].DeckNumber),
x: Number(jsonData.Deck[i].x),
y: Number(jsonData.Deck[i].y),
});
rowData.save(function(err) {
if (err) return console.log(err);
for (var i = 0; j = jsonData.Units.length, i < j; i++) {
(function(i) {
var unit = new MapDatabase({
//UnitID: mongoose.ObjectId(jsonData.Units[i].UnitID),
UnitID: jsonData.Units[i].UnitID,
TypeID: Number(jsonData.Units[i].TypeID),
x: Number(jsonData.Units[i].x),
y: Number(jsonData.Units[i].y),
_id: mongoose.Types.ObjectId(jsonData.Units[i].Code + 'dd40c86762e0fb12000003'),
MainClass: jsonData.Units[i].MainClass,
Orientation: jsonData.Units[i].Orientation,
Postion: jsonData.Units[i].Postion,
Deck: String(rowData._id)
});
unit.save(function(err) {
if (err) return console.log(err);
console.log('save');
});
})(i);
}
});
})(i);
}
callback();
};
app.get("/Map", function(req, res) {
console.log("got");
var urlTempBox = 'http://localhost:3000/MapCreate';
DeckDatabase.find(null, function(err, data) {
if (err) {
throw (err);
}
if (data.length != 0) {
MapDatabase.find()
.populate('Deck')
.exec(function(err, finalData) {
res.send(finalData);
});
} else {
request(urlTempBox, data, function(error, response, body) {
if (error) {
throw (error);
} else {
var jobj = JSON.parse(response.body);
console.log("persist begin");
persistMAP(jobj, function() {
console.log('retrieve Done');
});
}
});
}
});
You can use javascript callbacks like this:
User.findById(user_id,function(err,data){
//Inside this you can call another function.
});
console.log("Hello"); //this statement won't wait for the above statement
The more good approach would be use promises.
You can also use async await function to handle asynchronus tasks.
Async Await Style
async function getData(){
let user_data=await User.findById(user_id);
let user_videos=await Videos.findById(user_data._id); //user_data._id is coming from the above statement
}
But you can only use await in async methods.
Hope it helps.

Async callback on a mongodb function

I am trying to make this function make an appropriate callback. As it is written, the callback will be called twice - once for the synchronous 'if' statement and one for the asynchronous 'test2.save' statement. I am putting the counter code in just as an example that I tried. It doesn't work since the bottom if statement is synchronous. I already know what is wrong with this code, but I have no idea about how to make it better.
var postMatches = function(user1, userResults, callback) {
User.find({}, {username: 1, testResults: 1}, (err, users) => {
var counter = 0;
users.forEach(function(user2){
counter++;
if(user1 !== user2.username && user2.testResults !== undefined) {
var test1 = new Test({
username: user1,
match: user2.username,
compatability: mbti[userResults][user2.testResults],
alreadyMatches: false
});
test1.save( () => {
var test2 = new Test({
username: user2.username,
match: user1,
compatability: mbti[user2.testResults][userResults],
alreadyMatches: false
});
test2.save( () => {
if(counter === users.length) {
callback();
}
});
})
} else {
if(counter === users.length) {
callback();
}
}
})
})
};
From the comments and questions, compiled a code here. Use async module and forEach function to iterate over users list and return callback once done. Read about async and forEach. Let me know if this works for your use case.
var async = require('async')
var postMatches = function(user1, userResults, callback) {
User.find({}, {username: 1, testResults: 1}, (err, users) => {
var counter = 0;
async.forEach(users,function(user2,iterate_callback){
if(user1 !== user2.username && user2.testResults !== undefined) {
var test1 = new Test({
username: user1,
match: user2.username,
compatability: mbti[userResults][user2.testResults],
alreadyMatches: false
});
test1.save( () => {
var test2 = new Test({
username: user2.username,
match: user1,
compatability: mbti[user2.testResults][userResults],
alreadyMatches: false
});
test2.save( () => {
iterate_callback();
});
})
} else {
iterate_callback();
}
},function(err){
console.log("Done iterating");
return callback();
});
})
};

How to Send response after for each loop completed

Response.json should execute after foreach loop completes its execution
var todoarr = (req.body.data) ? req.body.data : undefined
todoarr.forEach(function(element) {
if(element.done == true) {
TodoService.removeTodo(element, function(success) {
});
}
});
res.json("success");
You can try to use async.js http://caolan.github.io/async/ .
each method http://caolan.github.io/async/docs.html#each
Or you can try use Promise.all.
For example:
let promiseArr = [];
todoarr.forEach(function(element) {
if(element.done == true) {
promiseArr.push(somePromiseMethod(element));
}
});
//now execute promise all
Promise.all(promiseArr)
.then((result) => res.send("success"))
.catch((err) => res.send(err));
More info here. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Some promise example:
function somePromiseMethod(element) {
return new Promise((resolve,reject) => {
TodoService.removeTodo(element, function(success) {
resolve();
});
});
}
Hope this helps.
You can't send multiple responses on single request, the only thing you can do it's a single response with the array of results:
es with async:
const async = require('async')
// FIX ME: this isn't correctly handled!
const todoarr = (req.body.data) ? req.body.data : undefined
let results = []
async.each(todoarr, function(element, callback) {
console.log('Processing todo ' + element)
if(element.done == true) {
TodoService.removeTodo(element, function(err, success) {
if(err){
callback(err)
} else {
results.push(success)
callback(null, success)
}
})
}
}, function(err) {
if(err) {
console.log('A element failed to process', err)
res.status(500).json(err)
} else {
console.log('All elements have been processed successfully')
// array with the results of each removeTodo job
res.status(200).json(results)
}
})
You can send the response inside the callback function of forEach.
Modify your function so that it will call res.json() on the last iteration only.
Example:
var todoarr = (req.body.data) ? req.body.data : undefined
todoarr.forEach(function(element,index) {
if(element.done == true) {
TodoService.removeTodo(element, function(success) {
});
}
if(index==todoarr.length-1){
res.json("success");
}
});
However, it may not be according to coding standards but it can definitely solve the problem.

Collect asynchronous and synchronous data in loop

var data = [10,21,33,40,50,69];
var i = 0;
var dataSeq = [];
while(i<data.length){
if(data[i]%2 == 0){
store.findOne({'visibility': true},function(err, data){
dataSeq.push(i)
i++;
});
}
else{
dataSeq.push(i)
i++;
}
}
if(i==data.length-1){
console.log(dataSeq) // Should Print [1,2,3,4,5]
return res.status(200).send({ message: 'Task Completed'})
}
I want to collect data as per loop excecutes.
I am aware about how to handle async calls in nodejs. But I want the callbacks in sequence.
e.g. Though there is a async call in if condition i want to hault the loop, so that I can push value of i in dataSeq and it will result in [1,2,3,4,5] array. I want that sequence because my post operations are dependent on that sequence.
I think asyncjs#eachSeries has what you need.
Your code would become something like this:
async.each(data, (item, callback) => {
if(item%2 == 0){
store.findOne({'visibility': true},function(err, data){
dataSeq.push(i)
i++;
});
}
else{
dataSeq.push(i)
i++;
}
}, (err) => {
// if any of the callbacks produced an error, err would equal that error
});
You can use something like async#eachOf
var async = require('async');
var data = [10,21,33,40,50,69];
var dataSeq = [];
async.eachOf(data, function(value, key, cb) {
if (value % 2 == 0) {
store.findOne({ 'visibility': true })
.then(function(doc) {
dataSeq.push(key);
})
.catch(function(err) {
return cb(err);
});
} else {
cb();
}
}, function(err) {
if (err) {
console.error(err)
return res.status(500).send(); # handle the error as you want
}
return res.status(200).send({ message: 'Task Completed'})
})

Async-WaterFall not working as expected

waterfall function with two calls but the second on is not waiting for the first one to completely finish. The first one has a mongodb.find() call in it.
Here is the async-waterfall function
app.get("/news", function(req, res) {
async.waterfall([
function (callback) {
var blogs = tendigiEngine.getAllBlogs(callback);
callback(null, blogs);
},
function (blogs, callback) {
var array = tendigiEngine.seperateBlogs(blogs, callback);
callback(null, array );
}
], function (err, result) {
// result now equals 'done'
console.log("done");
console.log(result);
});
});
Here are the two functions being called:
getAllBlogs():
exports.getAllBlogs = function() {
Blog.find(function(err, theBlogs){
if(!err) {
return theBlogs;
}
else {
throw err;
}
});
}
seperateBlogs():
exports.seperateBlogs = function(blogs) {
if(blogs.length === 0 ) {
return 0;
}
else {
blogs.reverse();
var blog = blogs[0];
blogs.shift();
var finArray = [blog, blogs];
return finArray;
}
console.log("asdf");
}
It is important that seperateBlogs won't be called before getAllBlogs() has returned theBlogs, but it is being called before the value is returned. I used Async_Waterfall to avoid this problem but it keeps recurring, which means I am using it wrong. What am I doing wrong here and how can I fix it?
Thanks!
Your exported functions are missing the callback parameters:
exports.getAllBlogs = function(cb) {
Blog.find(cb);
};
exports.seperateBlogs = function(blogs, cb) {
if (blogs.length === 0 )
return cb(null, blogs);
blogs.reverse();
var blog = blogs[0];
blogs.shift();
cb(null, [blog, blogs]);
}
Then your main code can be simplified as well:
async.waterfall([
tendigiEngine.getAllBlogs,
tendigiEngine.seperateBlogs
], function (err, result) {
// result now equals 'done'
console.log("done");
console.log(result);
});

Categories

Resources