tc39 / proposal-cancelable-promises

Former home of the now-withdrawn cancelable promises proposal for JavaScript
Other
375 stars 29 forks source link

Alternative proposal for cancelation to omit additional argument #22

Closed saschanaz closed 8 years ago

saschanaz commented 8 years ago

PS: This proposal continues here

I tried making a short proposal for cancelation as I don't like that I have to pass a new object for cancelation (related to https://github.com/zenparsing/es-cancel-token/issues/2). This proposal uses chain object to chain and cancel promises. This is not a firm proposal but I think it can be food for for further thought.

I post this here because it also affects the parameter list of Promise constructor callback.

API Design difference

new Promise((resolve, reject, chain) => { /* ... */ });

The following is the structure of chain object.

interface CancelableChain {
  /*
   * `chain()` stores cancelable promises and cancel them all
   * if its underlying promise gets canceled.
   */
  (promise): void;

  cancel(): void; // same as current `cancel` parameter to shorten the parameter list

  isCanceled: boolean; // true when underlying promise is canceled
  whenCanceled: Promise<void>; // resolves when underlying promise gets canceled
  /*
   * throws CancelError when underlying promise gets canceled, otherwise returns nothing
   */
  throwIfCanceled: void;
}

Use

function inner() {
  return new Promise(async (resolve, reject, chain) => {
    await a();
    chain.throwIfCanceled();
    await b();
    resolve();
  });
}

function outer() {
  return new Promise(async (resolve, reject, chain) => {
    await chain(inner()); // cancels inner() when parent promise gets canceled
    resolve();
  });
}
function inner() {
  return new Promise(async (resolve, reject, chain) => {
    await a();
    if (!chain.isCanceled) {
      await b();
    }
    resolve();
  });
}
function inner() {
  return new Promise(async (resolve, reject, chain) => {
    chain.whenCanceled.then(() => reject());
    await a();
    resolve();
  });
}

Syntax sugar

A cancelable function has a new chain keyword in its function scope.

cancelable function inner() {
  await a();
  chain.throwIfCanceled(); // chain as keyword, a form like `new.target`
  await b();
}

cancelable function outer() {
  chain inner(); // store inner() promise to cancellation chain
}
cancelable function inner() {
  await a();
  if (!chain.isCanceled) { // chain as keyword
    await b();
  }
}
benjamingr commented 8 years ago

Hey, something similar to this was suggested to the TC in the May meeting and got declined and shot down because some committee members believe it lets one modify behavior by the consumer.

domenic commented 8 years ago

This is a separate proposal, and not an issue with the proposal being developed in this repo. So, I'll close this issue. You're welcome to continue using it as a discussion thread for your proposal, but in general there are better places to do that, such as es-discuss or your own GitHub repo you created for your proposal.

As far as I can tell your proposal fails to solve the basic question, "how do I cancel a fetch?" With the proposal being developed in this repo, it's

const { token, cancel } = CancelToken.source();
fetch(url, { cancelToken: token });

cancelButton.onclick = cancel;

Whereas your proposal allows marking functions as cancelable, but doesn't allow actually canceling them in any way.

saschanaz commented 8 years ago

Thank you for your advice, I will continue this here.