2

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.

3
  • 1
    Doing async work in a constructor is really awkward, why not move that work to a prototype function? Also on an unrelated note, why are you making a shared array data? It seems like you're using data as a per-instance variable instead of one that's shared amongst all Obj instances. Commented Dec 16, 2014 at 13:44
  • @mscdex data is different from each object :) I can move the asynchronous work to another prototype method. So how can I be sure that the async work is done ? :) Commented Dec 16, 2014 at 13:57
  • 1
    Set a flag on the instance that then you check in your other prototype functions that rely on data. Also, to make data a per-instance variable you can remove Obj.prototype.data = []; and just add this.data = []; right inside your constructor. Or maybe you can use that as your flag -- initially set this.data = undefined; then in your prototype functions do something like: if (!this.data) throw new Error('No data'); Commented Dec 16, 2014 at 14:15

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.