-
Notifications
You must be signed in to change notification settings - Fork 0
Cancellation Samples
Ron Buckton edited this page Apr 2, 2015
·
3 revisions
// if canceled, leaves Promise in a forever pending state.
function delay(ms: number, token = CancellationToken.none) {
return new Promise<void>(resolve => {
let reg = token.register(() => { clearTimeout(handle); });
let handle = setTimeout(() => { reg.unregister(); resolve(); }, ms);
});
}
// if canceled, rejects Promise with the provided cancellation reason.
function fetch(url, token = CancellationToken.none) {
return new Promise<string>((resolve, reject) => {
let reg = token.register((reason) => { xhr.abort(); reject(reason); });
let xhr = new XMLHttpRequest();
xhr.onload = () => { reg.unregister(); resolve(xhr.responseText); };
xhr.onerror = () => { reg.unregister(); reject(xhr.statusText); };
xhr.open("GET", url);
xhr.send(null);
});
}
// async loop, cancellation ends the loop
function loop(callback, token = CancellationToken.none) {
const step = () => {
if (token.canceled) {
return Promise.resolve();
}
return Promise.resolve(callback()).then(step);
}
return step();
}
// subordinate cancellation - root can cancel a graph
class DownloadManager {
constructor() {
// root cts
this.cts = new CancellationTokenSource();
}
download(url, token = CancellationToken.none) {
// create a source linked to the provided token and the root
let linked = new CancellationTokenSource(this.cts.token, token);
return fetch(url, linked.token);
}
close() {
// cancel all pending fetches.
this.cts.cancel();
}
}
// separation of control - prevent intermediary callers from cancelling a graph
function outer() {
// caller cancellation of root, callee cannot cancel root
let root = new CancellationTokenSource();
// forward read-only token of root to intermediate caller
let result = intermediate(root.token);
// caller can cancel result through root
root.cancel();
}
function intermediate(token = CancellationToken.none) {
// cannot directly cancel token (in case it is shared)
// if intermediate needs to control cancellation of inner, it should create a linked source
let result = inner(token);
// cannot directly cancel result (in case it is memoized)
// if intermediate needs to intercept cancellation of result, it can wrap it in a new promise
return result;
}
function inner(token = CancellationToken.none) {
// cannot directly cancel token (in case it is shared)
// can observe cancellation and react
return new Promise((resolve, reject) => { token.register(reject); });
}
// observe reason for cancellation
function exec(token = CancellationToken.none) {
token.register(reason => {
// here we can observe why we were canceled
});
}
let source = new CancellationTokenSource();
exec(source.token);
source.cancel(new Error("User canceled."));
// cancellation in an async function
async function fetchAndParse(urls, token = CancellationToken.none) {
let result = [];
for (let url of urls) {
// early exit via throw if we were canceled at start or between each parse
token.throwIfCanceled();
let text = await fetch(url, token);
let parsed = parse(text);
result.push(parsed);
}
return result;
}
// update registration in multi-phase process
async function runSteps(token = CancellationToken.none) {
let slideIn = new SlideInAnimation();
// reverse the slide-in animation if token is canceled before the animation completes.
let slideInReg = token.register(() => slideIn.reverse());
await slideIn.animate();
// slide-in completed, unregister the callback
slideInReg.unregister();
let fadeOut = new FadeOutAnimation();
// reverse the fade-out animation if token is canceled before the animation completes.
let fadeOutReg = token.register(() => fadeOut.reverse());
await fadeOut.animate();
// fade-out completed, unregister the callback
fadeOutReg.unregister();
}