设计模式以实现依赖于其他插件的插件
Design pattern to implement plugins that depend on other plugins
我正在编写一个工具,该工具每 10 分钟由用户手动输入一些数据,并对每个输入运行一组计算。插件 A 提供一种计算,插件 B 提供另一种计算,依此类推。大多数情况下,这些插件彼此独立,即顺序无关紧要,因为每个插件的计算都会返回一个整数,该整数与其他插件的整数相加。
但是假设现在,我确实有一个插件C,它取决于插件A的返回是否为非零。在数据方面,假设我知道如何使插件A的状态可用于插件C.(例如,如果它是C++,我会使插件A成为插件C的friend
。但是,我用Javascript编写这个,所以我可能会采取更宽松的方法。我的问题更多是关于排序/依赖的模式。如何确保插件 A 的计算先于插件 C 运行?
当然,最简单的方法是按照插件需要运行的顺序简单地"安装"插件,即将插件以正确的顺序插入数组中,这样循环遍历所述数组就不需要思考了。
但是随着我添加越来越多的插件(根据场景,超过 20 个,也许是 30 个),这可能会变得脆弱。我想要更强大的东西。
我现在最好的主意是:
- 在"安装"插件时,我提供了它所依赖的插件数组。
- 每个插件都有一个静态成员,例如
_complete
,指示它是否已运行,并在每次新的迭代(用户输入)时重置。 - 当我遍历每个插件时,我会检查每个插件的依赖项
_complete
状态;如果一个不完整,那么我还没有运行计算;循环将是一个while
循环,在尝试所有其他插件后返回重试此插件。我还将有一个最大重试次数保护,以防止无限循环。
如何改进这一点?
正如Gothdo所建议的那样,Promises可以很好地解决这样的问题。但是,您的代码不需要异步。使用 promise 时,混合和匹配异步和同步代码没有任何约束。如果您运行小型同步函数的负载,您最终将支付性能开销,但对于数十个函数的用例,开销可以忽略不计。
承诺为将来发生的事情提供控制流抽象:当它完成时运行它。主要用于异步代码。可能会有人说这是用机枪猎鸭子。我用一个关于重新发明轮子(或从 GitHub 深处找到一个晦涩的六角形轮子)的论点来证明这个选择的合理性,如果需要的话,并且大多数 JS 程序员已经熟悉 Promise 并且库得到了很好的支持。最大的一个:围绕承诺进行必要的包装非常简单。
我快速勾勒了一下这样的包装器可能是什么样子。该代码使用有点丑陋的延迟模式来启用以任何顺序添加任务。随意添加错误处理,或以其他方式修改以满足您的需求。
function TaskRunner () {
this.tasks = {};
// name: String
// taskFn: fn(dep_1_result, dep_1_result...) -> result or Promise(result),
// deps: Optional, array of names or a name.
// Return: Promise over added task
TaskRunner.prototype.add = function (name, taskFn, deps) {
var self = this;
deps = (deps === undefined ? [] : (deps instanceof Array ? deps : [deps]));
name = name.toString();
self.tasks[name] = self.tasks[name] || {};
if(self.tasks[name].fn)
throw "Task " + name + " exists."
deps = deps.map(function (d) {
// Create result handler for deps, if none exist
self.tasks[d] = self.tasks[d] || {};
self.tasks[d].result = self.tasks[d].result || defer();
return self.tasks[d].result.promise;
});
// Create result handler for this task if none was created when
// handling deps of formely created tasks
self.tasks[name].result = self.tasks[name].result || defer();
// Excecute when all deps are done
self.tasks[name].fn = Promise.all(deps).spread(taskFn)
.then(function(res, err) {
// Trigger own result handler
if(err) {
self.tasks[name].result.reject(err);
throw err;
}
else {
self.tasks[name].result.resolve(res);
return res;
}
});
return self.tasks[name].fn;
}
}
示例用法:https://jsfiddle.net/3uL9chnd/4/
蓝鸟承诺库:http://bluebirdjs.com/docs/api-reference.html
编辑,免责声明:考虑开销时还有另一点:重置效率。如果您在较短的时间间隔内运行轻度计算,则为每个周期中的每个任务创建一个 promise 对象会使此方法不是最佳的。
- jQuery插件与其他基本的显示/隐藏功能冲突
- 如何在咕噜声插件中运行其他已注册的咕噜声任务
- jQuery插件干扰W/其他Javascript
- 设计模式以实现依赖于其他插件的插件
- Facebook wordpress 插件 - 如何加载 jssdk 并调用 FB.init,而不会与其他 facebo
- 使用 jQuery 工具提示插件(或任何其他方法)显示带有 PHP 对象属性的工具提示
- jQuery新闻自动收报机,隐藏在其他jQuery插件和图像后面
- JQuery插件不适用于codepen,在其他地方也很好
- 组织JQuery和其他插件的正确方式是什么
- 使用ocLazyLoad插件加载组件和其他依赖项
- 未捕获的语法错误与jquery插件和chrome,在其他浏览器工作正常
- JS插件在页面上不需要时破坏其他脚本
- 如何在不中断同一插件的其他实例的情况下取消订阅事件
- 如何在Jquery选择插件中搜索乌尔都语?有没有其他的方法来搜索乌尔都语在选择框
- 暂停除激活玩家外的所有其他玩家.jQuery音频插件
- 隐藏其他月份的日期在日期picker插件
- 使用javascript绘制动画线(没有Raphael或其他插件)
- JQuery 插件 - 回调会破坏其他功能
- Jquery插件,从其他方法调用函数
- 如何从其他插件调用jquery插件函数