Alternative way to achieve cancelable promise

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Alternative way to achieve cancelable promise

Kagami Rosylight

I want to find a way to replace cancellation token in the current [stage 1 cancelable promise proposal](https://github.com/tc39/proposal-cancelable-promises) with an alternative way which does not require passing an additional parameter.

 

Here is a short representation of [my current thought](https://github.com/SaschaNaz/cancelable):

 

```ts

// A cancelable object supports new `Symbol.cancel`.

// `object[Symbol.cancel]()` will cancel any tasks related to the object.

interface Cancelable {

  [@@cancel](): void;

}

 

interface Promise extends Cancelable {}

```

 

```js

// Here, a new `chain` object from promise constructor callback will

// help chaining cancelable tasks and provide cancellation related

// helper functions.

 

function foo() {

  return new Promise(async (resolve, reject, chain) => {

    await nonCancelableSubWork1();

    chain.throwIfCanceled(); // This line will throw `Cancel` object if the promise got a cancellation request

    await nonCancelableSubWork2();

    resolve();

 });

}

 

function bar() {

  return new Promise(async (resolve, reject, chain) => {

     // This `chain()` call will register foo() to the cancellation chain of a promise instance

     // and will propagate cancellation to the chained tasks.

     // The chain can receive any object that supports `Symbol.cancel`.

     await chain(foo());

     await chain(baz());

 });

}

 

const promise =  bar();

promise.cancel(); // This will cancel `bar` call and the cancellation will propagate to `foo` and `baz`

```

 

And with some syntax sugar for readability:

 

```js

cancelable function foo() {

  // `chain` is a keyword inside cancelable function blocks

  await nonCancelableSubWork1();

  chain.throwIfCanceled(); // similar form like `new.target`

  await nonCancelableSubWork2();

}

 

cancelable function bar() {

  chain foo();

  chain baz();

}

 

const promise = bar();

promise.cancel();

 

cancelable function baz() {

  try {

    chain baw();

  }

  else {

    // try-else block will catch cancellation, as the current existing proposal does

  }

}

```

 

I think this will achieve easier and more readable flow of cancelable tasks, what do you think?


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Alternative way to achieve cancelable promise

Marky Mark
I must admit that I do like this approach, however I'm not too familiar with the proposed spec to definitively comment on this. Have you tried proposing this in the https://github.com/tc39/proposal-cancelable-promises repository? That may be a better avenue for your suggestions.

On Sat, Oct 15, 2016 at 3:15 AM Kagami Rosylight <[hidden email]> wrote:

I want to find a way to replace cancellation token in the current [stage 1 cancelable promise proposal](https://github.com/tc39/proposal-cancelable-promises) with an alternative way which does not require passing an additional parameter.

 

Here is a short representation of [my current thought](https://github.com/SaschaNaz/cancelable):

 

```ts

// A cancelable object supports new `Symbol.cancel`.

// `object[Symbol.cancel]()` will cancel any tasks related to the object.

interface Cancelable {

  [@@cancel](): void;

}

 

interface Promise extends Cancelable {}

```

 

```js

// Here, a new `chain` object from promise constructor callback will

// help chaining cancelable tasks and provide cancellation related

// helper functions.

 

function foo() {

  return new Promise(async (resolve, reject, chain) => {

    await nonCancelableSubWork1();

    chain.throwIfCanceled(); // This line will throw `Cancel` object if the promise got a cancellation request

    await nonCancelableSubWork2();

    resolve();

 });

}

 

function bar() {

  return new Promise(async (resolve, reject, chain) => {

     // This `chain()` call will register foo() to the cancellation chain of a promise instance

     // and will propagate cancellation to the chained tasks.

     // The chain can receive any object that supports `Symbol.cancel`.

     await chain(foo());

     await chain(baz());

 });

}

 

const promise =  bar();

promise.cancel(); // This will cancel `bar` call and the cancellation will propagate to `foo` and `baz`

```

 

And with some syntax sugar for readability:

 

```js

cancelable function foo() {

  // `chain` is a keyword inside cancelable function blocks

  await nonCancelableSubWork1();

  chain.throwIfCanceled(); // similar form like `new.target`

  await nonCancelableSubWork2();

}

 

cancelable function bar() {

  chain foo();

  chain baz();

}

 

const promise = bar();

promise.cancel();

 

cancelable function baz() {

  try {

    chain baw();

  }

  else {

    // try-else block will catch cancellation, as the current existing proposal does

  }

}

```

 

I think this will achieve easier and more readable flow of cancelable tasks, what do you think?

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
--

mark

Sent while riding a hoverboard...
heyimmark.com :)


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

RE: Alternative way to achieve cancelable promise

Kagami Rosylight
In reply to this post by Kagami Rosylight

Thank you for your interest in my proposal. :D

 

I also thought so and I posted [an older version of my proposal on June](https://github.com/tc39/proposal-cancelable-promises/issues/22) there. I got this answer:

 

>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.

 

Thus I’m posting this here separately to gain attention and discuss.

 

From: [hidden email]
Sent: Wednesday, October 19, 2016 11:20 AM
To: [hidden email]; [hidden email]
Subject: Re: Alternative way to achieve cancelable promise

 

I must admit that I do like this approach, however I'm not too familiar with the proposed spec to definitively comment on this. Have you tried proposing this in the https://github.com/tc39/proposal-cancelable-promises repository? That may be a better avenue for your suggestions.

 

On Sat, Oct 15, 2016 at 3:15 AM Kagami Rosylight <[hidden email]> wrote:

I want to find a way to replace cancellation token in the current [stage 1 cancelable promise proposal](https://github.com/tc39/proposal-cancelable-promises) with an alternative way which does not require passing an additional parameter.

 

Here is a short representation of [my current thought](https://github.com/SaschaNaz/cancelable):

 

```ts

// A cancelable object supports new `Symbol.cancel`.

// `object[Symbol.cancel]()` will cancel any tasks related to the object.

interface Cancelable {

  [@@cancel](): void;

}

 

interface Promise extends Cancelable {}

```

 

```js

// Here, a new `chain` object from promise constructor callback will

// help chaining cancelable tasks and provide cancellation related

// helper functions.

 

function foo() {

  return new Promise(async (resolve, reject, chain) => {

    await nonCancelableSubWork1();

    chain.throwIfCanceled(); // This line will throw `Cancel` object if the promise got a cancellation request

    await nonCancelableSubWork2();

    resolve();

 });

}

 

function bar() {

  return new Promise(async (resolve, reject, chain) => {

     // This `chain()` call will register foo() to the cancellation chain of a promise instance

     // and will propagate cancellation to the chained tasks.

     // The chain can receive any object that supports `Symbol.cancel`.

     await chain(foo());

     await chain(baz());

 });

}

 

const promise =  bar();

promise.cancel(); // This will cancel `bar` call and the cancellation will propagate to `foo` and `baz`

```

 

And with some syntax sugar for readability:

 

```js

cancelable function foo() {

  // `chain` is a keyword inside cancelable function blocks

  await nonCancelableSubWork1();

  chain.throwIfCanceled(); // similar form like `new.target`

  await nonCancelableSubWork2();

}

 

cancelable function bar() {

  chain foo();

  chain baz();

}

 

const promise = bar();

promise.cancel();

 

cancelable function baz() {

  try {

    chain baw();

  }

  else {

    // try-else block will catch cancellation, as the current existing proposal does

  }

}

```

 

I think this will achieve easier and more readable flow of cancelable tasks, what do you think?

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

--

mark

Sent while riding a hoverboard...
heyimmark.com :)

 


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Alternative way to achieve cancelable promise

Bergi
In reply to this post by Kagami Rosylight
Kagami Rosylight wrote:
> I want to find a way to replace cancellation token in the current [stage 1 cancelable promise proposal](https://github.com/tc39/proposal-cancelable-promises) with an alternative way which does not require passing an additional parameter.

You're not the only one who is unsatisfied with the current proposal :-)
Also have a look at
* https://github.com/stefanpenner/random/tree/master/cancellation
* https://github.com/rtm/cancelable-promise
* https://github.com/bergus/promise-cancellation (mine)

> Here is a short representation of [my current thought](https://github.com/SaschaNaz/cancelable):
>
> ```ts
> // A cancelable object supports new `Symbol.cancel`.
> // `object[Symbol.cancel]()` will cancel any tasks related to the object.
> interface Cancelable {
>   [@@cancel](): void;
> }
>
> interface Promise extends Cancelable {}
> ```

That's not going to happen. Promises are result values that can be
passed to multiple consumers, and not every consumer should be allowed
to cancel the computation. So by default, promises must not be cancellable.
There could be such promises that can be cancelled by whomever gets a
hold on them - they are known as `Task`s iirc - but that needs to be an
opt-in.

> // Here, a new `chain` object from promise constructor callback will
> // help chaining cancelable tasks and provide cancellation related
> // helper functions.

I don't see the major difference between these "chain" objects and
"tokens" from the other proposals. Can you expand on that, please?

> function foo() {
>   return new Promise(async (resolve, reject, chain) => {
>     await nonCancelableSubWork1();
>     chain.throwIfCanceled(); // This line will throw `Cancel` object if the promise got a cancellation request
>     await nonCancelableSubWork2();
>     resolve();
>  });
> }

That's not going to work. You should never pass an `async function` to
the `new Promise` constructor, have a look
[here](http://stackoverflow.com/a/39409757/1048572).
Fortunately, the code in your actual proposal seems more reasonable here.

> And with some syntax sugar for readability:
>
> ```js
> cancelable function foo() {
>   // `chain` is a keyword inside cancelable function blocks
>   await nonCancelableSubWork1();
>   chain.throwIfCanceled(); // similar form like `new.target`
>   await nonCancelableSubWork2();
> }
>
> cancelable function bar() {
>   chain foo();
>   chain baz();
> }
>
> const promise = bar();
> promise.cancel();
> ```

If I understood correctly, your `chain` keyword could be used like
`await`? What is the difference between them?

But I really like the idea of `cancelable function` sugar that does the
housekeeping implicitly and returns cancellable promises automatically.
This very much reminds me of my own ideas
https://github.com/bergus/promise-cancellation/blob/master/enhancements.md 
:-)

kind regards,
  Bergi
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Re: Alternative way to achieve cancelable promise

Kagami Rosylight
In reply to this post by Kagami Rosylight

Hi Bergi:

 

>You're not the only one who is unsatisfied with the current proposal :-) Also have a look at

 

Great! I’m yet to read them but I definitely will soon to discuss more.

 

>Promises are result values that can be passed to multiple consumers, and not every consumer should be allowed to cancel the computation. So by default, promises must not be cancellable.

 

My current updated proposal allows cancellation only for promises created by `Promise.cancelable()` call. In this case, simple `return Promise.resolve(cancelablePromise)` will disallow cancellation for other consumers. But yes, this may be an opt-out rather than opt-in for APIs returning cancelable promises.

 

>I don't see the major difference between these "chain" objects and "tokens" from the other proposals. Can you expand on that, please?

 

“Chain”s do not need to be passed manually. A chain will store cancelable promises and will `[@@cancel]()` them after cancellation request. Each promise will have its own chain which will again propagate cancellations.

 

>You should never pass an async function to the new Promise constructor, have a look here. Fortunately, the code in your actual proposal seems more reasonable here.

 

I agree that the constructor approach is not good and would break easily. Thus, my updated proposal now uses `Promise.cancelable(async (chain) => {})` rather than the constructor itself.

 

>If I understood correctly, your chain keyword could be used like await? What is the difference between them?

 

`await` will not be automatically canceled when `chain` will:

 

```

cancelable function saya() {

  // This is theoretically cancelable but user somehow want it to be ‘atomic’.

  await cancelableSubWork();

  chain.throwIfCanceled();

}

```

 

Sincerely,

Kagami Sascha Rosylight


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss