I have a ("micro-task"?) queue which tasks are promises. You can schedule them like this:
const task = schedule(queue, _ => {
return /* valuable stuff */;
}).then(value => { /* do stuff with value */ });
now let's say that at some point I figure I don't need to do this task. I could just issue:
task.cancel()
I read about it being not possible to cancel promises in ES6 or something like that. But why can't I just help myself as follows?
function schedule(queue, fn) {
let index;
const promise = new Promise((resolve, reject) => {
index = queue.push(() => {
try {
resolve(fn());
} catch(error) {
reject(error);
}
});
});
promise.cancel = () => {
queue[index] = _ => {}; // just replace that callback with a no-op
}
return promise;
}
What is wrong with that? [apart from the fact that the index changes as the queue is worked and cannot be used to refer to the task, but that's a minor issue.]
Would it lead to some kind of memory leak because there is a Promise that remains pending forever? There is no longer a handle to the resolver function. Does the Promise hold on to the resolver function? And if so, does it use a weak reference? How do Promises keep themselves from being garbage collected if nobody is holding on to them, but they are simply expecting the then() or finally() to be called?
UPDATE Not sure this is really relevant, but Bergi asked the fair question that I haven't shows who works my queue. Let's just say, at some point something is working the queue.
In fact, I have made a Queue class from Array that has schedule and run:
class Queue extends Array {
schedule(fn) {
let index;
const promise = new Promise((resolve, reject) => {
index = this.push((...args) => {
try {
resolve(fn(...args));
} catch(error) {
reject(error);
}
});
});
promise.cancel = () => {
this[index] = _ => {}; // just remove that callback
}
return promise;
}
run() {
console.log("run", arguments);
while(this.length > 0)
this.shift()(...arguments);
}
}
now I can do
const queue = new Queue()
and
const task = queue.schedule(_ => {
return /* valuable stuff */;
}).then(value => { /* do stuff with value */ });
and then something might work the queue at some point.
function go() {
if(queue.length > 0)
setTimeout(function() {
queue.run(...arguments);
}
}
I was going to ask, what it even means to be a "micro-task" queue. But I didn't want to make this question become unfocused. I note that when a task adds another item to the queue, that gets executed in the same run. But when the action in the then() adds another item to the queue, then that is executed only in the next run.