次のような状況に最適に対処するにはどうすればよいですか?
完了するまでに時間がかかるコンストラクターがあります。
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.
3つのオプションがありますが、それぞれが普通ではないように見えます。
One、コールバックをコンストラクタに追加します。
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);
});
Two、EventEmitterを使用して 'loaded'イベントを発行します。
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);
});
または、コンストラクターをブロックします。
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)
しかし、私はこれのいずれも以前に見たことがありません。
他にどんなオプションがありますか?
更新2:これは、非同期ファクトリーメソッドを使用した更新された例です。 N.B.ブラウザで実行する場合は、Node 8またはBabelが必要です。
class Element {
constructor(nucleus){
this.nucleus = nucleus;
}
static async createElement(){
const nucleus = await this.loadNucleus();
return new Element(nucleus);
}
static async loadNucleus(){
// do something async here and return it
return 10;
}
}
async function main(){
const element = await Element.createElement();
// use your element
}
main();
更新:以下のコードは数回支持されました。ただし、静的メソッドを使用したこのアプローチははるかに優れています: https://stackoverflow.com/a/24686979/2124586
Promiseを使用したES6バージョン
class Element{
constructor(){
this.some_property = 5;
this.nucleus;
return new Promise((resolve) => {
this.load_nucleus().then((nucleus) => {
this.nucleus = nucleus;
resolve(this);
});
});
}
load_nucleus(){
return new Promise((resolve) => {
setTimeout(() => resolve(10), 1000)
});
}
}
//Usage
new Element().then(function(instance){
// do stuff with your instance
});
できることの1つは、すべてのニュークリアスをプリロードすることです(非効率かもしれません。どのくらいのデータかはわかりません)。もう1つは、プリロードがオプションではない場合にお勧めしますが、ロードされたニュークリアスを保存するためのキャッシュを備えたコールバックが含まれます。そのアプローチは次のとおりです。
Element.nuclei = {};
Element.prototype.load_nucleus = function(name, fn){
if ( name in Element.nuclei ) {
this.nucleus = Element.nuclei[name];
return fn();
}
fs.readFile(name+'.json', function(err, data) {
this.nucleus = Element.nuclei[name] = JSON.parse(data);
fn();
});
}
私は非同期コンストラクタを開発しました:
function Myclass(){
return (async () => {
... code here ...
return this;
})();
}
(async function() {
let s=await new Myclass();
console.log("s",s)
})();
私の最初の反復は:
たぶんコールバックを追加するだけ
匿名の非同期関数を呼び出してから、コールバックを呼び出します。
function Myclass(cb){
var asynccode=(async () => {
await this.something1();
console.log(this.result)
})();
if(cb)
asynccode.then(cb.bind(this))
}
私の2回目の繰り返しは:
コールバックの代わりにプロミスを試してみましょう。私は自分自身に考えました:奇妙な約束が約束を返し、それはうまくいきました。 ..そのため、次のバージョンは単なる約束です。
function Myclass(){
this.result=false;
var asynccode=(async () => {
await new Promise (resolve => setTimeout (()=>{this.result="ok";resolve()}, 1000))
console.log(this.result)
return this;
})();
return asynccode;
}
(async function() {
let s=await new Myclass();
console.log("s",s)
})();
古いjavascriptのコールバックベース
function Myclass(cb){
var that=this;
var cb_wrap=function(data){that.data=data;cb(that)}
getdata(cb_wrap)
}
new Myclass(function(s){
});
これは悪いコード設計です。
主な問題は、あなたのインスタンスのコールバックで、それがまだ「リターン」を実行しないことです、これは私が意味することです
var MyClass = function(cb) {
doAsync(function(err) {
cb(err)
}
return {
method1: function() { },
method2: function() { }
}
}
var _my = new MyClass(function(err) {
console.log('instance', _my) // < _my is still undefined
// _my.method1() can't run any methods from _my instance
})
_my.method1() // < it run the function, but it's not yet inited
したがって、適切なコード設計は、クラスをインスタンス化した後に「init」メソッド(または、場合によっては「load_nucleus」)を明示的に呼び出すことです。
var MyClass = function() {
return {
init: function(cb) {
doAsync(function(err) {
cb(err)
}
},
method1: function() { },
method2: function() { }
}
}
var _my = new MyClass()
_my.init(function(err) {
if(err) {
console.error('init error', err)
return
}
console.log('inited')
// _my.method1()
})
非同期部分を流extractなメソッドに抽出します。慣例により、私はそれらを一緒に呼びます。
_class FooBar {
constructor() {
this.foo = "foo";
}
async create() {
this.bar = await bar();
return this;
}
}
async function bar() {
return "bar";
}
async function main() {
const foobar = await new FooBar().create(); // two-part constructor
console.log(foobar.foo, foobar.bar);
}
main(); // foo bar
_
new FooBar()
をラッピングする静的なファクトリアプローチを試みました。 FooBar.create()
が、継承ではうまく機能しませんでした。 FooBar
をFooBarChild
に拡張しても、FooBarChild.create()
はFooBar
を返します。一方、私のアプローチではnew FooBarChild().create()
はFooBarChild
を返し、create()
を使用して継承チェーンを簡単にセットアップできます。