设计模式以实现依赖于其他插件的插件

时间:2016-03-14 08:51:09

标签: javascript design-patterns plugins dependencies

我正在编写一个工具,每10分钟由用户手动输入一些数据,并对每个输入运行一组计算。插件A提供一个计算,插件B提供另一个计算,依此类推。这些插件大多是彼此独立的,顺序并不重要,因为每个插件的计算都返回一个整数,该整数与其他插件相加。整数。

但是现在假设,我确实有一个插件C依赖于插件A的返回是否为非零。在数据方面,我们说我知道如何使Plugin A的状态可用于插件C.(如果它是C ++,我将插件A设为插件C的friend例如。但是,我在Javascript中写这个,所以我可能采取更宽松的方法。)我的问题更多的是关于排序/依赖的模式。 如何确保插件A的计算在插件C之前运行?

当然,最简单的方法是简单地安装"插件按照他们需要运行的顺序,即。以正确的顺序将插件插入到一个数组中,因此循环遍历所述数组的循环不需要思考。

但随着我添加越来越多的插件(超过20,可能是30,取决于场景),这可能会变得脆弱。我想要更强大的东西。

我现在最好的想法是:

  1. 安装"安装"一个插件,我提供了一个依赖它的插件数组。
  2. 每个插件都有一个静态成员,比如_complete,表示它是否已运行,并在每次新迭代(用户输入)时重置。
  3. 当我遍历每个插件时,我会检查每个插件的依赖性_complete状态;如果一个人没有完成,那么我还没有进行计算;循环将是一个while - 循环,在尝试所有其他插件后返回重试此插件。我还会有一个最大重试保护,以防止无限循环。
  4. 如何改进?

1 个答案:

答案 0 :(得分:1)

正如哥特多建议的那样,Promises可以很好地解决这样的问题。但是,您的代码不需要异步。使用promises时,混合和匹配异步和同步代码没有任何限制。如果运行大量小型同步函数,最终会支付性能开销,但是使用几十个函数的用例开销可以忽略不计。

Promise为将来发生的事情提供控制流抽象:运行时,完成后。主要用于异步代码。可能会争论说这是用迷你枪猎鸭。我通过关于重新发明轮子(或从GitHub的深处找到一个模糊的六角轮)的论证来证明这个选择,如果需要的话,支持异步准备,以及大多数JS程序员已经熟悉Promises和库的事实很好的支持。最大的一个:围绕承诺制作必要的包装非常简单。

我快速勾勒出这样一个包装器的样子。代码使用一些丑陋的延迟模式来启用任何顺序的任务。随意添加错误处理,或以其他方式修改以满足您的需求。

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/

Bluebird承诺lib:http://bluebirdjs.com/docs/api-reference.html

编辑,免责声明:在考虑开销时还有另一点:重置效率。如果您在紧密的时间间隔内运行光计算,则在每个周期为每个任务创建一个promise对象会使这种方法不是最佳的。