I want to do the following with NodeJS. Make an object array of the following, where each object has different local variables where they want to get when they initialize.
obj.js
var Obj = function (_id) {
this.id = _id;
var that=this;
db.getData(_id,function(collection){ //this method is asynchronous
collection.toArray(function(err, items) {
that.data=items;
});
});
}
Obj.prototype.data = [];
module.exports = Obj;
app.js
var arr=[];
arr.push(new obj(24));
arr.push(new obj(41));
arr.push(new obj(24));
arr.push(new obj(42));
//then do tasks with the arr
But since the arr constructor is synchronous they may not have get all the data when I do calculations with the arr. So how to handle this scenario ? I want to make sure that all the objects are successfully created before doing any work with them.
Thanks in advance.
2 Answers 2
guy, what @mscdex said is correct.
Firstly, in your code, data will be shared in memory, you should use this.data=[] in constructor.
Secondly, as @mscdex said, move your method into prototype, say
Obj.prototype.load=function(){
//code here...
}
then your code my like below:
var Obj = function(_id){
this.id=_id;
this.data = [];
}
Obj.prototype.load = function(){
var that = this;
db.getData(this.id,function(collection){ //this method is asynchronous
collection.toArray(function(err, items) {
that.data=items;
});
});
return that;
}
finally, your question, how do you know all of them are ready.
Obj.prototype.ready = [];
Obj.prototype.loaded=function(){
this.ready.push(1);
if(this.ready.length == Obj.target)
Obj.onComplete();
}
Obj.notifyme = function(callback,len){
Obj.target = len;
Obj.onComplete = callback;
}
the above code set an array to count load complete instances(use array because basic value could not be read from an instance's __proto__ reference). So what you should do is add this event(function) to load, so the code at last may be as following:
Obj.prototype.load = function(){
var that = this;
db.getData(this.id,function(collection){ //this method is asynchronous
collection.toArray(function(err, items) {
that.data=items;
that.loaded();
});
});
return that;
}
var args = [24,41,42];
Obj.notifyme(function(){/*then do tasks with the arr*/},args.length);
var arr = args.map(function(arg){
return new Obj(arg).load();
});
tell function notifyme the callback work and the number of instances. The last problem is if you do this routine more than one times you should reset target and callback since they are Obj global things.
Comments
I would make use of Promise pattern. Here is prototype of such an approach. I used native browser Promise for implementation and testing. In case of NodeJS you can use Kowal's Q implementation with similar (or very close) API.
var Obj = function (_id) {
this.id = _id;
var that = this;
this.promise = function() {
return new Promise(function(resolve) {
db.getData(_id, function (collection) {
collection.toArray(function (err, items) {
that.data.push(items);
resolve(that);
});
});
});
}();
};
Obj.prototype.data = [];
function objFactory(args) {
var promises = args.map(function(el) {
return new Obj(el).promise;
});
return Promise.all(promises);
}
objFactory([24, 25, 41, 42]).then(function(arr) {
console.log(arr, arr[0].data);
});
The idea is that when the promise object returned by objFactory resolves it will invoke corresponding then callback, inside of which it's reliable to work with data since it's populated for sure by that time.
Here is browser demo I was testing code with. You will have to adopt it for NodeJS environment. But the idea remains the same.
For example using Kris Kowal's Q it will be something like this:
this.promise = function() {
var deferred = Q.defer();
db.getData(_id, function (collection) {
collection.toArray(function (err, items) {
that.data.push(items);
deferred.resolve(that);
});
});
return deferred.promise;
}();
and Q.all instead of Promise.all.
Comments
Explore related questions
See similar questions with these tags.
data? It seems like you're usingdataas a per-instance variable instead of one that's shared amongst allObjinstances.data. Also, to makedataa per-instance variable you can removeObj.prototype.data = [];and just addthis.data = [];right inside your constructor. Or maybe you can use that as your flag -- initially setthis.data = undefined;then in your prototype functions do something like:if (!this.data) throw new Error('No data');