cancelable promises
Investigations and a proposed direction
Problem statement
Discussion roadmap
What is cancelation?
A third promise state
We’ll discuss how cancelation should be represented, independent of how it should be generated
How to cancel
Two approaches
Cancelable promises (“tasks”) vs. cancel tokens.
Next steps
Stage 1?
Is the committee on board with our conclusions? Can we proceed?
Cancelation as a third state
What does cancelation mean?
Is a canceled operation “successful”? Did it “fail”?
Canceled as a special case of rejection: bad
try {� await fetch(...);�} catch (e) {� if (!(e instanceof CancelError)) {� showUserMessage("The site is down.");� }�} finally {� stopLoadingSpinner();�}
Canceled as a third state: good
try {� await fetch(...);�} catch (e) {� showUserMessage("The site is down.");�} finally {� stopLoadingSpinner();�}
Making it a first-class third state
Third state in action
const canceledPromise = Promise.resolve().then(() => {� throw cancel "the user clicked 'stop'";�});��const rejectedPromise = Promise.cancel().cancelCatch(() => {� throw new Error("bad things happened");�});��const fulfilledPromise = Promise.cancel().cancelCatch(() => {� return 5;�});��// Cancelation propagates unless explicitly reacted to:�const canceledPromise2 = Promise.cancel()� .then(v => { ... })� .catch(e => { ... })� .finally(() => { ... });
Summary
Cancelation as a third state
But, this isn’t useful by itself. How does something become canceled?
Canceling async operations
The story so far
I had a dream: cancelable promise objects (“tasks”)
startSpinner();��const p = fetch(url)� .then(r => r.json())� .then(data => fetch(data.otherUrl))� .then(r => r.text())� .then(text => updateUI(text))� .catch(err => showUIError())� .finally(stopSpinner);��cancelButton.onclick = () => p.cancel();
Problems with tasks
The well-known alternative: cancel tokens
Cancel tokens in action
async function f(cancelToken) {� await a();�� // Only execute `b` if task has not been cancelled.� if (!cancelToken.requested)� await b();�}��const ct = new CancelToken(cancel => {� cancelButton.onclick = cancel;�});��f(ct); // NOTE: will be fulfilled if they click the cancel button
The cancel token API
new CancelToken(cancel => { … }) // revealing constructor��cancelToken.requested // boolean��cancelToken.promise // fulfills when requested��cancelToken.cancelIfRequested(reason);�// `throw cancel reason` if requested
Cancel tokens in action
async function f(cancelToken) {� await a();� cancelToken.cancelIfRequested();� await b();�}��const ct = new CancelToken(cancel => {� cancelButton.onclick = cancel;�});��f(ct); // NOTE: will be canceled if they click the cancel button
Advantages of cancel tokens
Disadvantages of cancel tokens
Summary
How to cancel
Cancel tokens are our best bet. They have some good advantages, and livable disadvantages.
The full proposal
A two-part proposal
Stage 1?
We’d like to move fast
Thank you