Can you write this without using a Deferred?

后端 未结 2 1362
难免孤独
难免孤独 2020-12-20 03:54

I wrote some code below that uses promises and the easiest way I could find to write it was using a Deferred object instead of the usual Promise executor functi

2条回答
  •  無奈伤痛
    2020-12-20 04:35

    Here is one solution that doesn't expose the promise resolver function anywhere outside the promise executor function.

    Following up on my comment to my own question about an event-based solution, here's what I came up with. It uses a triggered event and an event listener to cause an action inside the promise executor function.

    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);
                });
            }
        }
    }
    

    I was initially concerned about maintaining FIFO ordering so that the first one to call get() gets the next worker available. But, because eventListeners are called in the order they were added, I think this would actually achieve FIFO order. If there are multiple calls to get(), they will all get notified about the workerAdded, but after the first one handles the message and takes the worker, the others will just find no worker left for them so their listener will stay attached waiting for a future workerAdded message when there is a worker for them (when their listener gets to be first in line).

    I don't think I necessarily like this better than the other options shown, but it is an alternative and doesn't use Deferreds or even expose the resolve handler outside the executor function.


    As was suggested, this could also be done where the eventEmitter is an instance variable rather than a base class:

    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);
                });
            }
        }
    }
    

提交回复
热议问题