Skip to content

Instantly share code, notes, and snippets.

@getify
Last active November 5, 2020 01:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save getify/aef532057da25f934ce23f21c50c8d40 to your computer and use it in GitHub Desktop.
Save getify/aef532057da25f934ce23f21c50c8d40 to your computer and use it in GitHub Desktop.
trying to make race/all combinators for AbortController signals that aren't leaky
class cancelToken {
constructor(controller = new AbortController()) {
this.controller = controller;
this.signal = controller.signal;
({
pr: this.signal.pr,
rej: this.rej,
} = signalPromise(this.signal));
this.signal.pr.catch(() => {
this.controller.abort();
this.rej = this.signal = this.signal.pr = this.controller = null;
});
}
abort() {
this.rej();
}
}
function signalPromise(signal) {
if (signal.pr) {
return {
pr: signal.pr,
};
}
var ret = {};
ret.pr = new Promise((res,rej) => {
signal.addEventListener("abort",rej,false);
ret.rej = rej;
});
ret.pr.cleanup = () => {
if (signal) {
signal.removeEventListener("abort",ret.rej,false);
signal = null;
}
if (ret) {
ret = ret.pr = ret.pr.cleanup = ret.rej = null;
}
};
ret.pr.catch(ret.pr.cleanup);
return ret;
}
function signalRace(signals) {
var token = new cancelToken();
var prs = signals.map(signal => signalPromise(signal).pr);
var cleanups = prs.map(pr => pr.cleanup);
Promise.race(prs).catch(() => {
if (token) {
token.abort();
token = null;
}
if (cleanups) {
cleanups.forEach(fn => fn());
cleanups = null;
}
});
prs = null;
return token.signal;
}
function signalAll(signals) {
var token = new cancelToken();
var prs = signals.map(signal => signalPromise(signal).pr);
var cleanups = prs.map(pr => pr.cleanup);
Promise.all(
prs.map(pr => pr.catch(e => e))
).then(() => {
if (token) {
token.abort();
token = null;
}
if (cleanups) {
cleanups.forEach(fn => fn());
cleanups = null;
}
});
prs = null;
return token.signal;
}
var a = new cancelToken();
var b = new AbortController();
var c = new AbortController();
var d = new AbortController();
var e = signalAll([a.signal,b.signal,c.signal,d.signal]);
e.pr.catch(() => { console.log("aborted!"); });
c.abort();
a.abort();
b.abort();
d.abort();
// aborted!
var a = new cancelToken();
var b = new AbortController();
var c = new AbortController();
var d = new AbortController();
var e = signalAll([a.signal,b.signal,c.signal,d.signal]);
e.pr.catch(() => { console.log("aborted!"); });
b.abort();
// aborted!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment