Asynchrone constructeur
Comment puis-je mieux gérer une situation semblable à la suivante?
J'ai un constructeur qui prend un certain temps pour terminer.
var Element = function Element(name){
this.name = name;
this.nucleus = {};
this.load_nucleus(name); //This might take a second.
}
var oxygen = new Element('oxygen');
console.log(oxygen.nucleus); //Returns {}, because load_nucleus hasn't finished.
Je vois trois options, chaque de ce qui semble hors de l'ordinaire.
Un, ajouter un rappel du constructeur.
var Element = function Element(name, fn){
this.name = name;
this.nucleus = {};
this.load_nucleus(name, function(){
fn(); //Now continue.
});
}
Element.prototype.load_nucleus(name, fn){
fs.readFile(name+'.json', function(err, data) {
this.nucleus = JSON.parse(data);
fn();
});
}
var oxygen = new Element('oxygen', function(){
console.log(oxygen.nucleus);
});
Deux, utilisez EventEmitter à émettre un "chargé" de l'événement.
var Element = function Element(name){
this.name = name;
this.nucleus = {};
this.load_nucleus(name); //This might take a second.
}
Element.prototype.load_nucleus(name){
var self = this;
fs.readFile(name+'.json', function(err, data) {
self.nucleus = JSON.parse(data);
self.emit('loaded');
});
}
util.inherits(Element, events.EventEmitter);
var oxygen = new Element('oxygen');
oxygen.once('loaded', function(){
console.log(this.nucleus);
});
Ou trois, de bloquer le constructeur.
var Element = function Element(name){
this.name = name;
this.nucleus = {};
this.load_nucleus(name); //This might take a second.
}
Element.prototype.load_nucleus(name, fn){
this.nucleus = JSON.parse(fs.readFileSync(name+'.json'));
}
var oxygen = new Element('oxygen');
console.log(oxygen.nucleus)
Mais je n'ai pas vu tout cela avant.
Ce que d'autres options s'offrent à moi?
- Le blocage de l'constructeur moyens de blocage tout, donc je ne serais probablement pas le faire.
- Les Options 1 et 2 sont effectivement la même approche. Vous passez un gestionnaire de chargement pour le constructeur, et que le gestionnaire est déclenchée à partir de l'intérieur de la readFile de rappel. C'est de cette façon basés sur les événements de la programmation de travaux (qui est ce que JavaScript/Nœud). Vous pouvez utiliser l'option 1, ou 2, ou toute autre variation sur le thème, mais efficace, d'obtenir le même comportement asynchrone.
- J'ai appris beaucoup plus de choses à partir de cette "question" que les réponses données. +1 pour le "informatif question"!
- Juste une choses. Utilisation
.once()
pas.on()
pour ce cas. C'est sementically mieux :). - Je suis à la recherche d'un moyen de le faire bloquer avec ES6 Classes, est-ce possible? Merci
- Vous devriez pas de bloc. Utiliser des promesses à la place.
Vous devez vous connecter pour publier un commentaire.
Compte tenu de la nécessité d'éviter tout blocage dans le Nœud, l'utilisation d'événements ou de rappels n'est pas étrange(1).
Avec une légère modification de Deux, vous pouvez le fusionner avec Celui-ci:
Si, comme le
fs.readFile
dans votre exemple, le Nœud de base Api (au moins) souvent suivi le modèle des fonctions statiques qui exposent l'instance lorsque les données sont prêtes:(1) Il ne devrait pas prendre très longtemps pour lire un fichier JSON. Si elle l'est, peut-être un changement dans le système de stockage pour les données.
Mise à jour 2:
Voici une mise à jour de l'exemple à l'aide d'un asynchrone de la méthode de fabrique. N. B. cela nécessite Nœud de 8 ou Babel s'exécuter dans un navigateur.
Mise à jour:
Le code ci-dessous obtenu upvoted une couple de fois. Cependant, je trouve cette approche à l'aide d'une méthode statique beaucoup mieux:
https://stackoverflow.com/a/24686979/2124586
ES6 version à l'aide de promesses
Une chose que vous pourriez faire est de précharger toutes les noyaux (peut-être inefficace; je ne sais pas combien de données il est). L'autre, que je recommande si préchargement n'est pas une option, impliquerait un rappel avec un cache pour enregistrer chargé des noyaux. Ici, c'est que l'approche:
J'ai développé un async constructeur:
ma 1ère itération a été:
peut-être juste ajouter un rappel
appel anonyme asynchrone en fonction,
puis d'appeler la fonction de rappel.
mon 2ème itération a été:
essayons une promesse au lieu d'un rappel.
J'ai pensé à moi-même: étrange, une promesse de retour d'une promesse, et cela a fonctionné. .. alors, la prochaine version est juste une promesse.
de rappel pour les anciens javascript
C'est une mauvaise conception du code.
Le principal problème est dans la fonction de rappel de votre exemple, il n'est pas toujours exécuter le "retour", c'est ce que je veux dire
Ainsi, le code de qualité de conception est d'appeler explicitement la "init" de la méthode (ou, dans votre cas, "load_nucleus") après avoir instancié la classe
J'ai extrait les async portions dans une méthode couramment. Par convention, je les appelle ensemble.
JS:
J'ai essayé statique usine approche emballage
new FooBar()
, par exempleFooBar.create()
, mais il n'a pas bien joué avec l'héritage. Si vous étendezFooBar
enFooBarChild
,FooBarChild.create()
retournera toujours unFooBar
. Alors qu'avec mon approchenew FooBarChild().create()
sera de retour d'uneFooBarChild
et il est facile pour l'installation d'une chaîne d'héritage aveccreate()
.Vous pouvez exécuter la fonction de constructeur avec async fonctions de manière synchrone via nsynjs. Voici un exemple pour illustrer:
index.js (principales de la logique d'application):
MyObject.js (définition de la classe, avec une lente constructeur):
wrappers.js (nsynjs-conscient wrapper autour de ralentir les fonctions callbacks):
Jeu complet de fichiers pour cet exemple peut être trouvé ici: https://github.com/amaksr/nsynjs/tree/master/examples/node-async-constructor