至于这个简单的框架设计的有效性,也许你认为它很糟糕,但我真的只是感兴趣让它发挥作用,并通过艰难的方式了解为什么某些设计是好是坏。我想让它发挥作用,即使它的设计很糟糕。
所以我面临着一个挑战:如果定义一个服务依赖于另一个服务的代码在它所依赖的服务之前执行,它会认为该组件不存在。因为该代码尚未执行。我的目标是能够以任何顺序执行定义服务的代码。 希望这是有道理的。这个问题对我来说有点难以解释,但我的目标是编写这样的代码:
new Service('machine', ['machine / components / gear'], function(gear) {
alert('This machine depends on a ' + gear);
}).initialize();
new Service('machine / components / gear', ['machine / components / gear / components / cog'], function(cog) {
return 'gear which depends on a ' + cog;
});
new Service('machine / components / gear / components / cog', [], function(cog) {
return 'cog';
});
对于这个问题,提供给 Service 构造函数的第一个参数是服务的名称。按照我最初编写框架的方式,在这种情况下,
'machine'
在
'machine / components / gear'
之前执行,因此它看不到'machine / components / gear'
存在,同样,'machine / components / gear'
在'machine / components / gear / components / cog'
之前执行,因此无法使用该组件。注意,在显示代码之前:您会看到框架内使用的 count
变量,以及一些奇怪的东西,基本上这一切都非常简单和整洁,直到我开始尝试缓解我遇到的这个问题。正如您将看到的,我尝试通过以下方式解决问题:当发现依赖项不存在时,使用
setTimeout
并重试,直到 count
增加了几次,因此事件循环已完成几次之后,也许其他服务已经执行完毕。这很混乱,可能是一种糟糕的方法。这是代码:
var Directory = function() {
this.directories = {};
this.items = {};
};
var root = new Directory();
var Service = function(name, dependencies, func, count) {
if (!count) {
count = 0;
}
var that = this;
this.name = name;
this.wait = false;
this.dependencyCollection = [];
this.func = func;
this.initialize = function() {
setTimeout(function() {
console.log('Initializing with dependency collection: '); //debugging
console.log(that.dependencyCollection); //debugging
that.func.apply(null, that.dependencyCollection);
}, 10);
};
this.eventHandlers = {};
this.trigger = function(name) {
if (that.eventHandlers[name]) {
that.eventHandlers[name]();
}
};
this.on = function(name, func) {
that.eventHandlers[name] = func;
};
this.mutate = function(dependencies, func) {
new Service(that.name, dependencies, func);
that.trigger('mutate');
};
for (var x = 0; x < dependencies.length; x++) {
var nameSegments = dependencies[x].split(' / ');
var service = root;
var servicePlaceholder = service;
for (var i = 0; i < nameSegments.length; i++) {
var segment = nameSegments[i];
if (i === nameSegments.length - 1) {
if (servicePlaceholder.items[segment]) {
console.log('Observing dependency: ' + servicePlaceholder.items[segment]());
this.dependencyCollection.push(servicePlaceholder.items[segment]());
} else {
if (count < 3) {
(function(count) {
setTimeout(function() {
count++;
new Service(name, dependencies, func, count);
}, 1)
})(count);
} else {
throw 'Dependent item missing: ' + JSON.stringify(servicePlaceholder.items, null, 3) + ' (' + dependencies + ": " + segment + ')';
}
}
} else if (servicePlaceholder.directories[segment]) {
servicePlaceholder = servicePlaceholder.directories[segment];
} else {
if (count < 3) {
(function(count) {
setTimeout(function() {
count++;
new Service(name, dependencies, func, count);
}, 1)
})(count);
} else {
throw 'Dependent directory missing: ' + JSON.stringify(servicePlaceholder.directories, null, 3) + ' (' + segment + ')';
}
}
}
}
var directory = root;
var directoryPlaceholder = directory;
var nameSegments = name.split(' / ');
for (var i = 0; i < nameSegments.length; i++) {
var segment = nameSegments[i];
if (i === nameSegments.length - 1) {
console.log('Observing dependency collection: ' + that.dependencyCollection);
directoryPlaceholder.items[segment] = function() {
return that.func.apply(null, that.dependencyCollection);
}
} else {
if (directoryPlaceholder.directories[segment]) {
directoryPlaceholder = directoryPlaceholder.directories[segment];
} else {
directoryPlaceholder.directories[segment] = new Directory();
directoryPlaceholder = directoryPlaceholder.directories[segment];
}
}
}
};
如果其中有任何内容没有意义,就假设这是缓解问题的徒劳尝试。我已经用它工作了好几个小时,最后决定我毫无进展并寻求帮助。如果有人能帮助我弄清楚如何使这个(也许很糟糕)模型正确执行,我真的很感激。
没有超时和计数器之类的代码示例,除非您按时间顺序定义组件,否则可以正常工作:
var Directory = function() {
this.directories = {};
this.items = {};
};
var root = new Directory();
var Service = function(name, dependencies, func) {
var that = this;
this.name = name;
this.wait = false;
this.dependencyCollection = [];
this.func = func;
this.initialize = function() {
that.func.apply(null, that.dependencyCollection);
};
this.eventHandlers = {};
this.trigger = function(name) {
if (that.eventHandlers[name]) {
that.eventHandlers[name]();
}
};
this.on = function(name, func) {
that.eventHandlers[name] = func;
};
this.mutate = function(dependencies, func) {
new Service(that.name, dependencies, func);
that.trigger('mutate');
};
for (var x = 0; x < dependencies.length; x++) {
var nameSegments = dependencies[x].split(' / ');
var service = root;
var servicePlaceholder = service;
for (var i = 0; i < nameSegments.length; i++) {
var segment = nameSegments[i];
if (i === nameSegments.length - 1) {
if (servicePlaceholder.items[segment]) {
console.log('Observing dependency: ' + servicePlaceholder.items[segment]());
this.dependencyCollection.push(servicePlaceholder.items[segment]());
} else {
throw 'Dependent item missing: ' + JSON.stringify(servicePlaceholder.items, null, 3) + ' (' + dependencies + ": " + segment + ')';
}
} else if (servicePlaceholder.directories[segment]) {
servicePlaceholder = servicePlaceholder.directories[segment];
} else {
throw 'Dependent directory missing: ' + JSON.stringify(servicePlaceholder.directories, null, 3) + ' (' + segment + ')';
}
}
}
var directory = root;
var directoryPlaceholder = directory;
var nameSegments = name.split(' / ');
for (var i = 0; i < nameSegments.length; i++) {
var segment = nameSegments[i];
if (i === nameSegments.length - 1) {
console.log('Observing dependency collection: ' + that.dependencyCollection);
directoryPlaceholder.items[segment] = function() {
return that.func.apply(null, that.dependencyCollection);
}
} else {
if (directoryPlaceholder.directories[segment]) {
directoryPlaceholder = directoryPlaceholder.directories[segment];
} else {
directoryPlaceholder.directories[segment] = new Directory();
directoryPlaceholder = directoryPlaceholder.directories[segment];
}
}
}
};
setTimeout(function() {
console.log(root);
}, 1000);
new Service('machine / components / gear / components / cog', [], function(cog) {
return 'cog';
});
new Service('machine / components / gear', ['machine / components / gear / components / cog'], function(cog) {
return 'gear which depends on a ' + cog;
});
new Service('machine', ['machine / components / gear'], function(gear) {
alert('This machine depends on a ' + gear);
}).initialize();
最后,要捕获依赖关系未解决的情况,请在所有脚本加载后使用
setTimeout
检查队列,并报告任何从未解决依赖关系的组件。
var Service = (function() {
var _registered = {};
var _waiting = [];
// Check if all dependencies have been resolved
setTimeout(function() {
_waiting.forEach(function(obj) {
throw new Error('Failed to resolve dependencies for ' + obj.name);
});
}, 1);
function allDependenciesLoaded(dependencies) {
return dependencies.every(function(dep) {
return !!_registered[dep];
});
}
function processWaiting() {
for (var i = _waiting.length - 1; i >= 0; i--) {
var obj = _waiting[i];
if (allDependenciesLoaded(obj.dependencies)) {
_waiting.splice(i, 1);
obj.func();
}
}
}
return function(componentName, dependencies, func) {
_registered[componentName] = func;
if (!allDependenciesLoaded(dependencies)) {
_waiting.push({
name: componentName,
dependencies: dependencies,
func: func
});
} else {
func();
}
processWaiting();
};
})();
new Service('D', ['this', 'one', 'will', 'fail'], function() {});
new Service('A', ['B', 'C'], function() {
console.log('A is ready');
});
new Service('B', [], function() {
console.log('B is ready');
});
new Service('C', [], function() {
console.log('C is ready');
});