I am currently working on a web application with node.js and I can't figure a context problem with the library async.
Here is a example of code of my application :
notification.prototype.save = function (callback) {
async.parallel([
// Save the notification and associate it with the doodle
function _saveNotification (done) {
var query = 'INSERT INTO notification (notification_id, user_id, doodle_id, schedule_id) values (?, ?, ?, ?)';
notification.db.execute(query, [ this.notification_id, this.user_id, this.doodle_id, this.schedule_id ], { prepare : true }, function (err) {
return done(err);
});
console.log("SAVE NOTIFICATION");
console.log("doodle_id", this.doodle_id);
}.bind(this),
// Save notification for the users with the good profile configuration
function _saveNotificationForUsers (done) {
this.saveNotificationForUsers(done);
}.bind(this)
], function (err) {
return callback(err);
});
};
So in this code, I have to use the bind method to bind the context of my object ( this ) because otherwise async change it. I got it.
But what I don't understand is why the code of this.saveNotificationForUsers does not work the same way :
notification.prototype.saveNotificationForUsers = function (callback) {
console.log("SAVE NOTIFICATION FOR USERS");
console.log("doodle id : ", this.doodle_id);
async.waterfall([
// Get the users of the doodle
function _getDoodleUsers (finish) {
var query = 'SELECT user_id FROM users_by_doodle WHERE doodle_id = ?';
notification.db.execute(query, [ this.doodle_id ], { prepare : true }, function (err, result){
if (err || result.rows.length === 0) {
return finish(err);
}
console.log("GET DOODLE USERS");
console.log("doodle id : ", this.doodle_id);
return finish(err, result.rows);
});
}.bind(this)
], function (err) {
return callback(err);
});
};
When I call the previous code, the first console.log is able to show me the "this.doodle_id" variable, which means the function knows the "this" context.
But the functions inside the waterfall call does not, even if I bind 'this' to them.
I figured a way to make it works by creating a 'me' variable which is equal to 'this' juste before I call waterfall, and by binding the functions with the 'me' variable' and not this, but I would like to understand why I am forced to do this when I use async.waterfall and not when I use async.parallel.
I hope I was clear with the description of my problem, if someone can help me understand it will be a great pleasure !
The problem you’re seeing has got nothing to do with parallel or waterfall but rather how in the waterfall case, you’re referencing this in the callback to notification.db.execute, whereas in the parallel case, there is only a call to done in there. You could use bind again to bind that callback as well:
async.waterfall([
function _getDoodleUsers (finish) {
//…
notification.db.execute(query, [ this.doodle_id ], { prepare : true }, function (err, result){
//…
}.bind(this)); // <- this line
}.bind(this)
], function (err) {
//…
});
Related
I got this code (using jquery)
function getData(applyFunction, callback) {
$.when(
$.getJSON("http://www.mocky.io/v2/59d72b5b120000c902cb1b4f"),
$.getJSON("http://www.mocky.io/v2/59d72b5b120000c902cb1b4f"),
).done(function(
firstData,
secondData
){
callback(applyFunction, {
firstDataToApply: { data: firstData.popup },
secondDataToApply: { data: secondData.popup }
})
})
}
Is there a way to catch separate errors from the $.getJSON part(or the when part), log those errors, and still be able to send both firstData and secondData(at the same time) to the callback function?
(I'm aware that if some or both $.getJSON fail I'd be sending empty data to the callback, and would have to null check before getting popup)
sorry for the confusing post, thanks in advance
Yes. Promises are pipelines and each handler in the pipeline can transform the value or error passing through, so you can turn a failure into a "success with null" if that's really want you want to do by adding a catch handler and returning null (or whatever value you want to transform the error into) from it. See the catch calls below and also the comments:
function getData(applyFunction, callback) {
$.when(
$.getJSON("http://www.mocky.io/v2/59d72b5b120000c902cb1b4f")
.catch(function(err) {
console.error(err);
return null;
}),
$.getJSON("http://www.mocky.io/v2/59d72b5b120000c902cb1b4f")
.catch(function(err) {
console.error(err);
return null;
})
).done(function(firstData, secondData) {
callback(applyFunction, {
firstDataToApply: {
data: firstData.popup // You'll need checks on this!
},
secondDataToApply: {
data: secondData.popup // And this!
}
});
});
}
Of course, if you're going to do this more than once or twice, you can avoid repeating yourself with a function:
function getJSONOrNull(url) {
return $.getJSON(url).catch(function(err) {
console.error(err);
return null;
});
}
then
function getData(applyFunction, callback) {
$.when(
getJSONOrNull("http://www.mocky.io/v2/59d72b5b120000c902cb1b4f"),
getJSONOrNull("http://www.mocky.io/v2/59d72b5b120000c902cb1b4f")
).done(function(firstData, secondData) {
callback(applyFunction, {
firstDataToApply: {
data: firstData.popup // You'll need checks on this!
},
secondDataToApply: {
data: secondData.popup // And this!
}
});
});
}
i'm creating a forum, and i want to get the number of message in one topic,
so in my method GetListTopic i get the list of topic, and after i want to know how many message there is in one topic so i request the table forum_message for every topic in my list=>
db.Forum_Topic.count({}, function (err, topicount) { //nombre de topic
db.Forum_Topic.find().skip(skipNumber).limit(nombreDeTopicParPage).sort({ dateLastMessage: -1 }, function (err, dbres) {
//dbres = liste de topic
for (var i in dbres)
{
db.Forum_Message.count({ idTopic: new mongojs.ObjectID(dbres[i]._id) }, function (err, Messagecount) {
dbres[i].TotalMessage = Messagecount;
});
}
res.send([dbres, topicount]);
});
});
my need is to add in every object of my list the total message => dbres[i].TotalMessage = Messagecount;
but that don't work dbres[i].TotalMessage is set correctly in the function callback but when i send object TotalMessage doesn't exist ....
someone can help me please ?
The problem is that you are using for loop within an async function. And that res.send() statement does not wait for the loop to complete. It executes before.
There is a solution by using this async library. There are also many options in this library, however, I think the async.each() is the one you can try.
Good luck.
i finally found the solution to foreach update with nodejs and mongodb,
i use async.js here : https://www.npmjs.com/package/async
and to delete a liste of mail
async.each(ListeIdMailToDelete, function (mailID, callback) {
db.userMessageMail.find({ _id: db.ObjectId(mailID) }, function (err, dbres) {
if (dbres.length > 0)
{
if (dbres[0].expediteur == newplayer.pseudo || dbres[0].destinataire == newplayer.pseudo) {
db.userMessageMail.remove({ _id: dbres[0]._id }, function () {
callback(err);
})
}
}
});
}, function (err) {
console.log(err);
});
Scenario
I have an app that allows users to create an account, but also allows the user's the ability to delete their account. Upon deletion of their account I have a Cloud Code function that will delete all of the "Post"s the user has made. The cloud code I am using is...
//Delete all User's posts
Parse.Cloud.define("deletePosts", function(request, response) {
var userID = request.params.userID;
var query = new Parse.Query(Parse.Post);
query.equalTo("postedByID", userID);
query.find().then(function (users) {
//What do I do HERE to delete the posts?
users.save().then(function(user) {
response.success(user);
}, function(error) {
response.error(error)
});
}, function (error) {
response.error(error);
});
});
Question
Once I have the query made for all of the user's posts, how do I then delete them? (see: //What do I do HERE?)
You could use
Parse.Object.destroyAll(users); // As per your code – what you call users here are actually posts
See: http://parseplatform.org/Parse-SDK-JS/api/classes/Parse.Object.html#methods_destroyAll
Also, consider using Parse.Cloud.afterDelete on Parse.User (if that is what you mean by "deleting account") to do cleanups such as these.
Oh, and just to be complete, you don't need the save() routine after destroyAll()
Updates in-line below below your "What do I do HERE..." comment:
NOTES:
You don't need to call the save() method, so I took that out.
This, of course, is merely a matter of personal preference, but you may want to choose a parameter name that makes a little more sense than "users", since you're really not querying users, but rather Posts (that just happen to be related to a user).
Parse.Cloud.define("deletePosts", function(request, response) {
var userID = request.params.userID;
var query = new Parse.Query(Parse.Post);
query.equalTo("postedByID", userID);
query.find().then(function (users) {
//What do I do HERE to delete the posts?
users.forEach(function(user) {
user.destroy({
success: function() {
// SUCCESS CODE HERE, IF YOU WANT
},
error: function() {
// ERROR CODE HERE, IF YOU WANT
}
});
});
}, function (error) {
response.error(error);
});
});
I'm new in node js. I'm using sails js as framework. I'm trying to use nested database query.
Please check the added code.
My current mongo collection name is todostatus. Form it i grabbed the data . Then I tried to use a callback function to iterate the data and update each item inside the data from database. But I'm not getting all the data. Can anyone please give me lead?
Thanks in advance,
Nixon
TodoStatus.find({todo_id : req.params.todo_id}).done(function(errs, responsible){
if(errs){
}
else{
function grabUsersName(todoRes, callback){
async.forEach(todoRes, function (item, callback2){
User.findOne ({id : item.user_id}).done(function (errses, user) {
if(errses)
{
console.log(errses);
}
else
{
item['name'] = user.name;
}
});
}, function(eerr) {
callback(todoRes);
});
}
grabUsersName(responsible, function(resultsa){
console.log(resultsa);
return res.view({
responsible: resultsa
})
})
}
})
I found the solution.
The issue is in the function grabUserName(). I missed to use callback for the async.forEach. Check the functions code for more information
function grabUsersName(todoRes, callback){
async.forEach(todoRes, function (item, callback2){
User.findOne ({id : item.user_id}).done(function (errses, user) {
if(errses){
console.log(errses);
}
else{
item['name'] = user.name;
callback2();
}
})
}, function(eerr) {
callback(todoRes);
});
}
I am trying to run these set of functions:
function erorr(e) {
// error getting database
alert(e.message);
}
window.onload = function() {
prepareDatabase(erorr);
};
function prepareDatabase(error) {
return openDatabase('tasks13', '', 'Offline task storage', 3*1024*1024, function (db) {
db.changeVersion('', '1.0', function (t) {
t.executeSql('CREATE TABLE tasks (id, detail,status)');
}, error);
});
}
But, after running this I get an error current version of the database and 'oldVersion' argument do not match.
Not sure what wrong I am doing here.
Correct code:
function prepareDatabase(error) {
return openDatabase('tasks13', '', 'Offline task storage', 3*1024*1024, function (db) {
db.changeVersion(db.version, '1.0', function (t) {
t.executeSql('CREATE TABLE tasks (id, detail,status)');
}, error);
});
}
While it's possible to open any available version of a WebSQL db (by passing an empty string as version identifier), you need to explicitly specify the current version of the db when calling db.changeVersion. The current version of the db is made available as db.version.
This is what the specification says:
Check that the value of the first argument to the changeVersion() method exactly matches the database's actual version. If it does not, then the preflight operation fails.
From http://www.w3.org/TR/webdatabase/#asynchronous-database-api
I ran into the same error.
I refrained from using db.changeVersion and used the following more imperative style of logic instead:
this.db = window.openDatabase('myDb', '1.0', 'a comment', 5*1024*1024);
if (this.db) {
this.db.transaction(function (tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS myTable(...)',
[],
function(tx, rs) { },
function(tx, err) { alert("Error in create table occurred: " + err) }
);
});
}
Hope it works for you as well.
/Fredrik