您可以不使用Deferred来编写此代码吗?

时间:2020-04-22 06:15:40

标签: javascript node.js promise deferred

我在下面编写了一些使用Promise的代码,而我能找到的最简单的写法是使用Deferred对象而不是通常的Promise执行程序函数,因为我需要从执行程序外部解析Promise。我想知道是否存在基于Promise执行程序功能的可接受的设计模式,以解决诸如此类的不使用类似延迟的解决方案的问题?无需从承诺执行者外部解决承诺就可以做到吗?

这是详细信息。

我有一个项目,该项目使用一组工作线程以及希望不时使用工作线程的代码的各个部分。为了解决这个问题,我创建了一个简单的WorkerList类,其中包含可用工作线程的列表。当某人想要使用一个时,他们在其上调用get()并返回一个解析为工作线程的promise。如果辅助线程立即可用,则诺言将立即解决。如果所有工作线程都在使用中(因此可用工作线程列表为空),则承诺将无法解析,直到以后通过add(worker)方法将其放回到可用列表中。

此WorkerList类只有两个方法,add(worker)get()。您get()是一个工作人员,当您完成工作后,就add(worker)将其退回。退回add(worker)后,该类将检查是否有任何任务在等待可用的Worker。如果存在,它将与可用的Worker达成其承诺。兑现了别人的诺言的地方就是使用了延期。

这是WorkerList的代码:

class WorkerList {
    constructor() {
        this.workers = [];
        this.deferredQueue = [];
    }
    add(worker) {
        this.workers.push(worker);

        // if someone is waiting for a worker,
        // pull the oldest worker out of the list and
        // give it to the oldest deferred that is waiting
        while (this.deferredQueue.length && this.workers.length) {
            let d = this.deferredQueue.shift();
            d.resolve(this.workers.shift());
        }
    }
    // if there's a worker, get one immediately
    // if not, return a promise that resolves with a worker
    //    when next one is available
    get() {
        if (this.workers.length) {
            return Promise.resolve(this.workers.shift());
        } else {
            let d = new Deferred();
            this.deferredQueue.push(d);
            return d.promise;
        }
    }
}

而且,这是Deferred实现:

function Deferred() {
    if (!(this instanceof Deferred)) {
        return new Deferred();
    }
    const p = this.promise = new Promise((resolve, reject) => {
        this.resolve = resolve;
        this.reject = reject;
    });
    this.then = p.then.bind(p);
    this.catch = p.catch.bind(p);
    if (p.finally) {
        this.finally = p.finally.bind(p);
    }
}

2 个答案:

答案 0 :(得分:2)

也许以下内容只是穷人的延期处理方法,并没有真正解决问题的症结所在,但是除了延期队列外,您还可以保留一系列解析程序功能。

这比您的方法节省了少量代码,并且避免了显式使用Deferreds。

我不知道是否存在已建立的模式,但是它本身似乎是用于维护异步对象池的可重用模式,因此您可以将其命名为{ {1}},然后将其组合为WorkerList中的可重用部分:

AsyncPool

答案 1 :(得分:2)

这是一种不会在promise执行程序功能之外的任何地方公开promise解析器功能的解决方案。

在我对基于事件的解决方案的问题提出自己的评论之后,我想到了以下内容。它使用触发的事件和事件侦听器在promise executor函数内部引起动作。

class WorkerList extends EventEmitter {
    constructor() {
        this.workers = [];
    }
    add(worker) {
        this.workers.push(worker);
        // notify listeners that there's a new worker in town
        this.emit('workerAdded');
    }
    // if there's a worker, get one immediately
    // if not, return a promise that resolves with a worker
    //    when next one is available
    get() {
        if (this.workers.length) {
            return Promise.resolve(this.workers.shift());
        } else {
            return new Promise(resolve => {
                const onAdded = () => {
                    if (this.workers.length) {
                        this.off('workerAdded', onAdded);
                        resolve(this.workers.shift());
                    }
                }

                this.on('workerAdded', onAdded);
            });
        }
    }
}

最初,我担心保持FIFO的顺序,以便第一个调用get()的人获得下一个可用的工作程序。但是,由于eventListener是按照添加顺序调用的,因此我认为这实际上将实现FIFO顺序。如果有多个对get()的呼叫,他们都会收到有关workerAdded的通知,但是在第一个处理完消息并接了工作人员之后,其他人只会找不到工作人员,因此他们当有工作人员时(当他们的听众排在第一位时),听众将保持连接状态,等待将来的workerAdded消息。

我认为我不一定比上面显示的其他选项更喜欢此选项,但这是一种替代选择,它不使用Deferreds甚至不在执行程序函数外部公开resolve处理程序。


如建议的那样,也可以在eventEmitter是实例变量而不是基类的情况下完成:

class WorkerList {
    constructor() {
        this.workers = [];
        this.emitter = new EventEmitter();
    }
    add(worker) {
        this.workers.push(worker);
        // notify listeners that there's a new worker in town
        this.emitter.emit('workerAdded');
    }
    // if there's a worker, get one immediately
    // if not, return a promise that resolves with a worker
    //    when next one is available
    get() {
        if (this.workers.length) {
            return Promise.resolve(this.workers.shift());
        } else {
            return new Promise(resolve => {
                const onAdded = () => {
                    if (this.workers.length) {
                        this.emitter.off('workerAdded', onAdded);
                        resolve(this.workers.shift());
                    }
                }

                this.emitter.on('workerAdded', onAdded);
            });
        }
    }
}