2014-12-16 2 views
2

Я хочу сделать следующее с помощью NodeJS. Создайте массив объектов из следующего, где каждый объект имеет разные локальные переменные, которые они хотят получить, когда они инициализируются.Создание массива объекта с асинхронным конструктором

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 

Но так как конструктор обр синхронное они не могут быть получить все данные, когда я делаю расчеты с обр , Итак, как справиться с этим сценарием? Я хочу, чтобы все объекты были успешно созданы, прежде чем выполнять какую-либо работу с ними.

Заранее спасибо.

+1

Выполнение работы async в конструкторе действительно неудобно, почему бы не перенести эту работу на функцию прототипа? Также на несвязанной ноте, почему вы делаете «данные» с общим массивом? Похоже, что вы используете 'data' как переменную для каждого экземпляра вместо того, который используется для всех экземпляров' Obj'. – mscdex

+0

Данные @mscdex отличаются от каждого объекта :) Я могу переместить асинхронную работу на другой метод прототипа. Итак, как я могу быть уверен, что работа async завершена? :) –

+1

Установите флажок в экземпляре, после чего вы проверите свои другие функции прототипа, которые полагаются на 'data'. Кроме того, чтобы сделать 'data' переменной per-instance, вы можете удалить' Obj.prototype.data = []; 'и просто добавить' this.data = []; 'прямо внутри вашего конструктора. Или, может быть, вы можете использовать это как свой флаг - изначально установить 'this.data = undefined; ', тогда в ваших прототипных функциях сделайте что-то вроде:' if (! This.data) throw new Error (' No data '); ' – mscdex

ответ

2

парень, что @mscdex сказал правильно. Во-первых, в вашем коде data будет использоваться в памяти, вы должны использовать this.data=[] в конструкторе. Во-вторых, как сказал @mscdex, переместите метод в прототип, скажем

Obj.prototype.load=function(){ 
    //code here... 
} 

тогда ваш код мой, как показано ниже:

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; 
} 

наконец, ваш вопрос, как вы знаете, все из них готовы ,

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; 
} 

приведенные выше код установка массива для подсчета нагрузки полных экземпляров (использование массива, так как базовое значение не может быть считано из __proto__ ссылки на экземпляр в). Так что вы должны сделать, это добавить это событие (функция) для load, поэтому код, наконец, может быть следующим:

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(); 
}); 

Телль функции обратного вызова notifyme работа и количество экземпляров. Последняя проблема заключается в том, что если вы выполняете эту процедуру более одного раза, вы должны сбросить target и callback, так как они являются Obj глобальными вещами.

1

Я бы использовал шаблон Promise. Вот прототип такого подхода. Я использовал собственный браузер Promise для внедрения и тестирования. В случае NodeJS вы можете использовать Q-реализацию Kowal с аналогичным (или очень близким) 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); 
}); 

Идея заключается в том, что, когда объект обещания возвращаемого objFactory решает, что будет вызывать соответствующие then обратный вызов, внутри которого это надежно работать с data, так как он населен точно к тому времени.

Это браузер demo Я тестировал код с помощью. Вам придется принять его для среды NodeJS. Но идея остается прежней.

Например, используя Q Kris Коваля в это будет что-то вроде этого:

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; 
}(); 

и Q.all вместо Promise.all.

Смежные вопросы