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
Maybe the below is just a poor man's approach to deferreds, and doesn't really get to the crux of the matter, but instead of a queue of deferreds, you could just keep a queue of resolver functions.
This saves a small amount of code over your approach and avoids explicitly using Deferreds.
I don't know if there is an established pattern for this, but this in itself seems like a reusable pattern for maintaining an asynchronous pool of objects, so rather than calling it WorkerList
, you could name it AsyncPool
, and then compose that as a reusable piece within your WorkerList
:
class AsyncPool {
constructor() {
this.entries = [];
this.resolverQueue = [];
}
add(entry) {
console.log(`adding ${entry}`);
this.entries.push(entry);
// if someone is waiting for an entry,
// pull the oldest one out of the list and
// give it to the oldest resolver that is waiting
while (this.resolverQueue.length && this.entries .length) {
let r = this.resolverQueue.shift();
r(this.entries.shift());
}
}
// if there's an entry, get one immediately
// if not, return a promise that resolves with an entry
// when next one is available
get() {
return new Promise((r) =>
this.entries.length
? r(this.entries.shift())
: this.resolverQueue.push(r)
);
}
}
let pool = new AsyncPool();
pool.add('Doc');
pool.add('Grumpy');
pool.get().then(console.log);
pool.get().then(console.log);
pool.get().then(console.log);
pool.get().then(console.log);
// add more entries later
setTimeout(() => pool.add('Sneezy'), 1000);
setTimeout(() => pool.add('Sleepy'), 2000);