2015-01-30 4 views
3

Я определяю класс, который создает несколько модулей, которые зависят от предыдущих. Сами модули могут потребовать операцию async, прежде чем они будут готовы (т. Е. Установить соединение mysql), поэтому я предоставил каждому конструктору обратный вызов, который будет вызываться, как только модуль будет готов. Однако я столкнулся с проблемой, когда инстанцирование классов, которые готовы немедленно:Javascript: шаблон конструктора async

var async = require('async'); 

var child = function(parent, cb) { 
    var self = this; 
    this.ready = false; 

    this.isReady = function() { 
     return self.ready; 
    } 

    /* This does not work, throws error below stating c1.isReady is undefined*/ 
    cb(null, true); 

    /* This works */ 
    setTimeout(function() {  
     self.ready = true; 
     cb(null, true); 
    }, 0); 
} 


var Parent = function(cb) { 
    var self = this; 
    async.series([ 
     function(callback){ 
      self.c1 = new child(self, callback);      
     }, 
     function(callback){ 
      self.c2 = new child(self, callback); 
     } 
    ], 
    function(err, results){ 
     console.log(self.c1.isReady(), self.c2.isReady); 
     console.log(err, results); 
    }); 
} 

var P = new Parent(); 

Я предполагаю, что вопрос вызов сЬ внутри конструктора означают асинхронный переходит к следующей функции перед вызовом конструктора отделки. Есть ли лучший подход к этому? Я решил использовать обещания, но я считаю, что этот подход легче понять/следовать.

+0

родственный: [? Это плохая практика, чтобы иметь конструктор функции возвращают Promise] (http://stackoverflow.com/q/24398699/1048572) – Bergi

+0

Возможный дубликат [ Асинхронный конструктор] (http://stackoverflow.com/questions/11856778/asynchronous-constructor) –

ответ

9

Вам придется отложить вызов обратного вызова, когда все синхронно, потому что любой код в обратном вызове пока не сможет ссылаться на объект (как вы видели). Поскольку конструктор не завершил выполнение, присвоение его возвращаемого значения еще не завершено.

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

В node.js, для достижения вашей цели будет более эффективным использование process.nextTick() вместо setTimeout(). См. this article для более подробной информации.

var child = function(parent, cb) { 
    var self = this; 
    this.ready = false; 

    this.isReady = function() { 
     return self.ready; 
    } 

    /* This works */ 
    process.nextTick(function() {  
     self.ready = true; 
     cb(null, true); 
    }, 0); 
} 

Вот общее наблюдение. Многие люди (включая меня) считают, что это слишком усложняет ситуацию, чтобы поместить какие-либо операции async в конструктор по причинам, связанным с этим. Вместо этого большинство объектов, которые должны выполнять операции async для настройки, будут предлагать метод .init() или .connect() или что-то в этом роде. Затем вы создаете объект, как обычно, синхронно, а затем отдельно инициируете асинхронную часть инициализации и передаете ему обратный вызов. Это полностью исключает вас.

Если вы хотите использовать обещания для отслеживания вашей операции асинхронного сканирования (что является отличным направлением функций), это поможет упростить конструктору возврат объекта и операцию .init(), чтобы вернуть обещание. Это становится беспорядочным, чтобы попытаться вернуть как объект, так и обещание от конструктора и даже messier, чтобы закодировать это.

Затем, используя обещания вы можете сделать это:

var o = new child(p); 
o.init().then(function() { 
    // object o is fully initialized now 
    // put code in here to use the object 
}, function(err) { 
    // error initializing object o 
}); 
0

Если вы не можете поместить объект в обещание, положить обещание в объект.

Дайте ему готовое свойство, которое является обещанием.

Если у вас есть иерархия классов (я говорю о виджетах сейчас) из виджетов, базовый класс может определить свойство Ready и назначить ему уже разрешенный объект Promise. Делая это базовый класс означает, что всегда можно писать код, как это

var foo = Foo(); 
foo.Ready.then(() => { 
    // do stuff that needs foo to be ready 
}); 

Производных классы могут взять под контроль разрешения обещания, заменив значение Ready с новым объектом обещания, и его решением, когда код асинхронного завершает ,

Вот комплексная проработка Asynchronous Constructor design pattern

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