Re: Cancellation architectural observations

2015-03-29 Thread Andrea Giammarchi
OK, just for record sake, I'v ecreated a working example of what I
previously meant.
https://gist.github.com/WebReflection/a015c9c02ff2482d327e

Here a basic example (something that will reject on timeout)
```js

// will be resolvednew Promise(function ($res, $rej, ifCanceled) {
  var internal = setTimeout($rej, 1000);
  ifCanceled(function () {
clearTimeout(internal);
  });
})// will be resolved without executing
.then(
  function () {
console.log('on time');
  },
  function () {
console.log('error');
  }
)
.cancel()// will simply execute and resolve
.then(function () {
  console.log('no time');
});

```

The idea is that a Promise is cancelable **only** if a function that
defines how to cancel is provided, otherwise there's no cancel-ability,
it's a regular Promise, and nothing is exported.

This still grants internal private/hidden cancelability through a
resolution or rejection but it also gives the ability to expose a
`.cancel()` to the outher world.

There's no forever pending problem because all Promises involved in the
chain will be silently resolved.
The code might be a bit convolute but it was mainly to provide a playground
and I don't see any real violation of the Promises principles.

The presence of `cancel` and its ability is a contract between the Promise
creator/provider and the external world.

Best Regards



On Fri, Mar 27, 2015 at 5:43 PM, Andrea Giammarchi 
andrea.giammar...@gmail.com wrote:

 following up from this
 https://github.com/whatwg/fetch/issues/27#issuecomment-86987752 , and
 most likely late to the party.

 How about cancel-ability done this way ?

 ```js

 var p = new Promise(function (res, rej, cancel) {
   // resolved in 1 second via random value
   var t = setTimeout(res, 1000, Math.random());
   // if meant to be canceled
   // we define internally what to do
   cancel(function () {
 clearTimeout(t);
   });
 });

 // whenever/if needed
 p.cancel().then(...);
 ```

 The exposed cancel-ability is arbitrary provided internally so that if
 missing, an error is thrown while if provided it sets the internal state of
 the promise as `canceled` in case it's different from `resolved` or
 `rejected`

 It gives the ability to react, if a `then` is attached, or the ability to
 ignore, if nothing happens to the returned, non cancel-able, Promise.

 This avoids exposing to the outer world what happens inside the Promise
 and provides arbitrary ability to cancel one.

 A cancel-able Promise is one that defined such behavior, which by default
 is throwing if that's not defined internally.
 This would solve already many cases I have in mind, via users, or
 UserAgent, and legitimately behind the scene without any need to expose any
 internal logic.

 How to resolve or throw other attached promises? Well, `p.cancel().then()`
 resolves, while `p.cancel().throw()` does not. `p.cancel()`, without then
 or throw would simply throw away pending promises as explicit `ignore`
 intent.

 How bad does it look and what am I screwing up in here in terms of
 Promises philosophy?

 Best Regards







 On Wed, Mar 4, 2015 at 8:01 PM, Ron Buckton ron.buck...@microsoft.com
 wrote:

  new Promise(resolve = doLater(resolve, cts.token)).then(handleResult);
  setImmediate(() = cts.cancel());

 
  In this scenario cancel would be called right after the resolve method
  is called, but before handlerResult is called. For this to work with a
  cancellation token you would need to pass the token to every step in
  the chain to both stop work being done and to ignore the
  result/prevent a handler from being called. Wouldn't it be better if
  the promise chain took care of this for the programmer?

 I can be convinced that CancellationToken registrations should be invoked
 asynchronously, though I wonder if then CancellationTokenSource#cancel
 should return a Promise to observe any errors that occur.

 Ron
 ___
 es-discuss mailing list
 es-discuss@mozilla.org
 https://mail.mozilla.org/listinfo/es-discuss



___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-29 Thread Frankie Bagnardi
I don't think this has been brought up, but isn't what we're doing here
bubbling an event?  Would it make sense to generalize this to a simple
event bubbling system?

function cancelableGet(url){
  const xhr = new XMLHttpRequest();
  return new Promise(function(resolve, reject){
xhr.open('get', url);
xhr

this.on('cancel', reason = {
  xhr.abort();
  reject(new CanceldRequest(reason));

  // if we wanted to pass it upwards
  this.emit('cancel', reason);
});
  });

}

cancelableGet('/foo')
.then(...)
.emit('cancel');

​

Our listener would be unbound as soon as the promise in cancelableGet settled.


This also allows for non-overlapping names for events (Symbol should be
permitted), where there may be multiple cancelable things in the chain.

It also allows additional metadata, or different event names when 'cancel'
can mean multiple things.  For example, a process.  Do we send sigint or
sigkill to cancel it?

Thoughts?



On Sun, Mar 29, 2015 at 8:56 AM, Andrea Giammarchi 
andrea.giammar...@gmail.com wrote:

 OK, just for record sake, I'v ecreated a working example of what I
 previously meant.
 https://gist.github.com/WebReflection/a015c9c02ff2482d327e

 Here a basic example (something that will reject on timeout)
 ```js

 // will be resolvednew Promise(function ($res, $rej, ifCanceled) {
   var internal = setTimeout($rej, 1000);
   ifCanceled(function () {
 clearTimeout(internal);
   });
 })// will be resolved without executing
 .then(
   function () {
 console.log('on time');
   },
   function () {
 console.log('error');
   }
 )
 .cancel()// will simply execute and resolve
 .then(function () {
   console.log('no time');
 });

 ```

 The idea is that a Promise is cancelable **only** if a function that
 defines how to cancel is provided, otherwise there's no cancel-ability,
 it's a regular Promise, and nothing is exported.

 This still grants internal private/hidden cancelability through a
 resolution or rejection but it also gives the ability to expose a
 `.cancel()` to the outher world.

 There's no forever pending problem because all Promises involved in the
 chain will be silently resolved.
 The code might be a bit convolute but it was mainly to provide a
 playground and I don't see any real violation of the Promises principles.

 The presence of `cancel` and its ability is a contract between the Promise
 creator/provider and the external world.

 Best Regards



 On Fri, Mar 27, 2015 at 5:43 PM, Andrea Giammarchi 
 andrea.giammar...@gmail.com wrote:

 following up from this
 https://github.com/whatwg/fetch/issues/27#issuecomment-86987752 , and
 most likely late to the party.

 How about cancel-ability done this way ?

 ```js

 var p = new Promise(function (res, rej, cancel) {
   // resolved in 1 second via random value
   var t = setTimeout(res, 1000, Math.random());
   // if meant to be canceled
   // we define internally what to do
   cancel(function () {
 clearTimeout(t);
   });
 });

 // whenever/if needed
 p.cancel().then(...);
 ```

 The exposed cancel-ability is arbitrary provided internally so that if
 missing, an error is thrown while if provided it sets the internal state of
 the promise as `canceled` in case it's different from `resolved` or
 `rejected`

 It gives the ability to react, if a `then` is attached, or the ability to
 ignore, if nothing happens to the returned, non cancel-able, Promise.

 This avoids exposing to the outer world what happens inside the Promise
 and provides arbitrary ability to cancel one.

 A cancel-able Promise is one that defined such behavior, which by default
 is throwing if that's not defined internally.
 This would solve already many cases I have in mind, via users, or
 UserAgent, and legitimately behind the scene without any need to expose any
 internal logic.

 How to resolve or throw other attached promises? Well,
 `p.cancel().then()` resolves, while `p.cancel().throw()` does not.
 `p.cancel()`, without then or throw would simply throw away pending
 promises as explicit `ignore` intent.

 How bad does it look and what am I screwing up in here in terms of
 Promises philosophy?

 Best Regards







 On Wed, Mar 4, 2015 at 8:01 PM, Ron Buckton ron.buck...@microsoft.com
 wrote:

  new Promise(resolve = doLater(resolve, cts.token)).then(handleResult);
  setImmediate(() = cts.cancel());

 
  In this scenario cancel would be called right after the resolve method
  is called, but before handlerResult is called. For this to work with a
  cancellation token you would need to pass the token to every step in
  the chain to both stop work being done and to ignore the
  result/prevent a handler from being called. Wouldn't it be better if
  the promise chain took care of this for the programmer?

 I can be convinced that CancellationToken registrations should be
 invoked asynchronously, though I wonder if then
 CancellationTokenSource#cancel should return a Promise to observe any
 errors that occur.

 Ron
 

Re: Cancellation architectural observations

2015-03-29 Thread Andrea Giammarchi
quickly at least one thought: `this` inside a Promise callback, is not the
Promise itself so nothing could possibly work.

Moreover, you are setting an event that does not cancel, it resolves.
Meaning all attached Promised will resolve too which is IMO not what
cancel mans in the dictionary.

My code brancches out resolving without executing, it's a **very**
different approach. It solves a forever pending problem, without
triggering. Also, in your example, if I trigger a `cancel` in a promise
created though `then` via the cancelable method, nothing would happen which
is undesired in a chained expected behavior.



On Sun, Mar 29, 2015 at 7:10 PM, Frankie Bagnardi f.bagna...@gmail.com
wrote:

 I don't think this has been brought up, but isn't what we're doing here
 bubbling an event?  Would it make sense to generalize this to a simple
 event bubbling system?

 function cancelableGet(url){
   const xhr = new XMLHttpRequest();
   return new Promise(function(resolve, reject){
 xhr.open('get', url);
 xhr

 this.on('cancel', reason = {
   xhr.abort();
   reject(new CanceldRequest(reason));

   // if we wanted to pass it upwards
   this.emit('cancel', reason);
 });
   });

 }

 cancelableGet('/foo')
 .then(...)
 .emit('cancel');

 ​

 Our listener would be unbound as soon as the promise in cancelableGet settled.


 This also allows for non-overlapping names for events (Symbol should be
 permitted), where there may be multiple cancelable things in the chain.

 It also allows additional metadata, or different event names when 'cancel'
 can mean multiple things.  For example, a process.  Do we send sigint or
 sigkill to cancel it?

 Thoughts?



 On Sun, Mar 29, 2015 at 8:56 AM, Andrea Giammarchi 
 andrea.giammar...@gmail.com wrote:

 OK, just for record sake, I'v ecreated a working example of what I
 previously meant.
 https://gist.github.com/WebReflection/a015c9c02ff2482d327e

 Here a basic example (something that will reject on timeout)
 ```js

 // will be resolvednew Promise(function ($res, $rej, ifCanceled) {
   var internal = setTimeout($rej, 1000);
   ifCanceled(function () {
 clearTimeout(internal);
   });
 })// will be resolved without executing
 .then(
   function () {
 console.log('on time');
   },
   function () {
 console.log('error');
   }
 )
 .cancel()// will simply execute and resolve
 .then(function () {
   console.log('no time');
 });

 ```

 The idea is that a Promise is cancelable **only** if a function that
 defines how to cancel is provided, otherwise there's no cancel-ability,
 it's a regular Promise, and nothing is exported.

 This still grants internal private/hidden cancelability through a
 resolution or rejection but it also gives the ability to expose a
 `.cancel()` to the outher world.

 There's no forever pending problem because all Promises involved in the
 chain will be silently resolved.
 The code might be a bit convolute but it was mainly to provide a
 playground and I don't see any real violation of the Promises principles.

 The presence of `cancel` and its ability is a contract between the
 Promise creator/provider and the external world.

 Best Regards



 On Fri, Mar 27, 2015 at 5:43 PM, Andrea Giammarchi 
 andrea.giammar...@gmail.com wrote:

 following up from this
 https://github.com/whatwg/fetch/issues/27#issuecomment-86987752 , and
 most likely late to the party.

 How about cancel-ability done this way ?

 ```js

 var p = new Promise(function (res, rej, cancel) {
   // resolved in 1 second via random value
   var t = setTimeout(res, 1000, Math.random());
   // if meant to be canceled
   // we define internally what to do
   cancel(function () {
 clearTimeout(t);
   });
 });

 // whenever/if needed
 p.cancel().then(...);
 ```

 The exposed cancel-ability is arbitrary provided internally so that if
 missing, an error is thrown while if provided it sets the internal state of
 the promise as `canceled` in case it's different from `resolved` or
 `rejected`

 It gives the ability to react, if a `then` is attached, or the ability
 to ignore, if nothing happens to the returned, non cancel-able, Promise.

 This avoids exposing to the outer world what happens inside the Promise
 and provides arbitrary ability to cancel one.

 A cancel-able Promise is one that defined such behavior, which by
 default is throwing if that's not defined internally.
 This would solve already many cases I have in mind, via users, or
 UserAgent, and legitimately behind the scene without any need to expose any
 internal logic.

 How to resolve or throw other attached promises? Well,
 `p.cancel().then()` resolves, while `p.cancel().throw()` does not.
 `p.cancel()`, without then or throw would simply throw away pending
 promises as explicit `ignore` intent.

 How bad does it look and what am I screwing up in here in terms of
 Promises philosophy?

 Best Regards







 On Wed, Mar 4, 2015 at 8:01 PM, Ron Buckton ron.buck...@microsoft.com
 wrote:

  new 

Re: Cancellation architectural observations

2015-03-27 Thread Andrea Giammarchi
following up from this
https://github.com/whatwg/fetch/issues/27#issuecomment-86987752 , and most
likely late to the party.

How about cancel-ability done this way ?

```js

var p = new Promise(function (res, rej, cancel) {
  // resolved in 1 second via random value
  var t = setTimeout(res, 1000, Math.random());
  // if meant to be canceled
  // we define internally what to do
  cancel(function () {
clearTimeout(t);
  });
});

// whenever/if needed
p.cancel().then(...);
```

The exposed cancel-ability is arbitrary provided internally so that if
missing, an error is thrown while if provided it sets the internal state of
the promise as `canceled` in case it's different from `resolved` or
`rejected`

It gives the ability to react, if a `then` is attached, or the ability to
ignore, if nothing happens to the returned, non cancel-able, Promise.

This avoids exposing to the outer world what happens inside the Promise and
provides arbitrary ability to cancel one.

A cancel-able Promise is one that defined such behavior, which by default
is throwing if that's not defined internally.
This would solve already many cases I have in mind, via users, or
UserAgent, and legitimately behind the scene without any need to expose any
internal logic.

How to resolve or throw other attached promises? Well, `p.cancel().then()`
resolves, while `p.cancel().throw()` does not. `p.cancel()`, without then
or throw would simply throw away pending promises as explicit `ignore`
intent.

How bad does it look and what am I screwing up in here in terms of Promises
philosophy?

Best Regards







On Wed, Mar 4, 2015 at 8:01 PM, Ron Buckton ron.buck...@microsoft.com
wrote:

  new Promise(resolve = doLater(resolve, cts.token)).then(handleResult);
  setImmediate(() = cts.cancel());

 
  In this scenario cancel would be called right after the resolve method
  is called, but before handlerResult is called. For this to work with a
  cancellation token you would need to pass the token to every step in
  the chain to both stop work being done and to ignore the
  result/prevent a handler from being called. Wouldn't it be better if
  the promise chain took care of this for the programmer?

 I can be convinced that CancellationToken registrations should be invoked
 asynchronously, though I wonder if then CancellationTokenSource#cancel
 should return a Promise to observe any errors that occur.

 Ron
 ___
 es-discuss mailing list
 es-discuss@mozilla.org
 https://mail.mozilla.org/listinfo/es-discuss

___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-04 Thread Alexander Fritze
I know ES is taking a different route here - keeping async sitting on
top of the language - but I can't resist wading in on the cancellation
discussion and give a perspective from our experience with Stratified
JavaScript (SJS).

One of the most surprising things we found is how crucial cancellation
is to make concurrent logic composable, and how easy it is to fold it
into the existing JavaScript base language.

TL;DR: If asynchronous code is recast as blocking code, then handling
cancellation/aborting cleanup is straightforward by leveraging and
extending the try/catch/finally construct.


 Background

SJS (http://stratifiedjs.org) is a project that has been going on for
the past 5 years or so, with the goal of folding async into the base
JS language, and building a homogeneous JS client/server framework on
top of it (https://conductance.io). We've gained a great deal of
experience with it, and we've been using it to build large web apps.

The basic idea behind SJS is to recast asynchronous code as blocking
code. E.g. we can make a blocking 'pause' function with the following
code:

  function pause(delay) {
waitfor() {
  setTimeout(resume, delay);
}
  }

(for more details on the waitfor-resume construct see
https://conductance.io/reference#sjs:%23language/syntax::waitfor-resume)

We can now use 'pause' to code blocking logic in a straightforward way, e.g.:

  for (var i=0; i100; ++i) {
document.getElementById('foo').left = i;
pause(100); // wait 100ms
  }

Similarly to 'pause', we can cast other asynchronous functions into
blocking form. E.g. a function that waits for a DOM event could look
like this:

  function waitforEvent(elem, event) {
waitfor(var rv) {
  elem.addEventListener(event, resume, true);
}
return rv;
  }

And a function that asynchronously fetches a document via
XMLHttpRequest like this:

  function httpGet(url) {
var request = new XMLHttpRequest()
request.open(GET, url, true);
waitfor() {
  request.onreadystatechange = function() {
if (request.readyState == 4) resume();
  };
  request.send(null);
}
return request.responseText;
  }


These can then be used as part of any normal JS control flow, e.g.:

  if (waitforEvent(document, 'click').target == some_dom_element) {
do_something_with_response(httpGet('http://foo.bar'))
  }



 Adding in concurrency

For performing multiple asynchronous codepaths simultaneously and
orchestrating their interactions, SJS adds a couple of 'structured
concurrency combinators' to JS: waitfor-and  waitfor-or.

E.g. simultanously kicking off a request to cnn or bbc and then
proceeding with the first result, looks something like this:

  var news;

  waitfor {
news = httpGet('http://cnn.com');
  }
  or {
news = httpGet('http://bbc.com');
  }

  ... now do something with the news ...


These constructs work very well with functional abstraction 
composition. E.g. defining the code above as 'function getNews()', we
can layer more concurrency around it:

  var result;

  waitfor {
result = getNews();
  }
  or {
waitforEvent(cancel_button, 'click');
result = 'The user cancelled!';
  }
  or {
pause(1);
result = 'Timeout!';
  }

  ...

Being able to abstract and meaningfully compose concurrent strands of
logic that themselves might be composed of complicated strands of
concurrent logic is very powerful: It allows us to build large
concurrent programs using a tractable functional decomposition
approach.



 Now the interesting bit: Retraction/Cancellation

Now, the problem is that with the 'pause', 'waitforEvent' and
'httpGet' functions defined as above, this piece of code will not
properly clean up after itself.
If e.g. the user aborts, the requests to cnn and bbc will still be
pending even though they are not needed any more, potentially wasting
server time and network resources.
Also, no matter which of the code paths 'wins', we'll always leak the
click event listener on the cancel_button.

But as it turns out, it is quite easy to fold
cancellation/abortion/cleanup into the language when asynchronous code
is sequentialized as it is in SJS.
.
The key is to realize two things:

1. 'being cancelled' is just another mode of exiting a block of code -
the others being via normal control flow or by an exception being
thrown, and

2. cancellation flow is similar to exception propagation. We want to
handle cancellation starting from the blocked site propagating upwards
along the call stack. A cancellation is really very similar to an
exception, the main difference being that an exception is generated at
the bottom of the call stack, whereas a cancellation is generated at
the top of the call stack.


Now, JS already has the try/catch/finally construct that handles
cleanup after an exception (catch clause), or unconditional cleanup
(finally clause), and this construct can be extended to handle the
cancellation case too:

SJS just ensures that 'finally' 

RE: Cancellation architectural observations

2015-03-04 Thread Ron Buckton
 new Promise(resolve = doLater(resolve, cts.token)).then(handleResult);
 setImmediate(() = cts.cancel());

 
 In this scenario cancel would be called right after the resolve method 
 is called, but before handlerResult is called. For this to work with a 
 cancellation token you would need to pass the token to every step in 
 the chain to both stop work being done and to ignore the 
 result/prevent a handler from being called. Wouldn't it be better if 
 the promise chain took care of this for the programmer?

I can be convinced that CancellationToken registrations should be invoked 
asynchronously, though I wonder if then CancellationTokenSource#cancel should 
return a Promise to observe any errors that occur. 

Ron
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-03 Thread Kevin Smith
 function doLater(callback, token) {
   var handle = setImmediate(callback);
   token.register(() = clearImmediate(handle));
 }

 var cts = new CancellationTokenSource();
 doLater(..., cts.token);
 cts.cancel();


I'm not really clear about the ordering properties of setImmediate relative
to the promise microtask queue, but:

function doLater(callback, cancelPromise) {
let handle;
cancelPromise.then(_= clearImmediate(handle));
handle = setImmediate(callback);
}

This should work if setImmediate uses either the same MT queue as promises
or a lower-priority queue.
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-03 Thread Marius Gundersen
On Tue, Mar 3, 2015 at 12:49 AM, Ron Buckton rbuck...@chronicles.org
wrote:

  ```js

 function doLater(callback, token) {

   setImmediate(() = {

 if (token.canceled) return;

 callback();

   });

 }

 ```


  But in this case, I still have a callback scheduled which may be
 unnecessary overhead. If this were a native API, we could chose to
 prioritize cancellation registrations over promise tasks. Even if the
 registrations are async, the cancellation signal would still need to be
 observable in a synchronous manner, even if only through reading the
 CancellationToken#canceled property.

Since cancellations are likely to be triggered asynchronously by the user,
it doesn't really matter if the cancellation signal is async or not, it
might still be too late to cancel the job. For example:

```js

new Promise(resolve = doLater(resolve, cts.token)).then(handleResult);
setImmediate(() = cts.cancel());
```
In this scenario cancel would be called right after the resolve method is
called, but before handlerResult is called. For this to work with a
cancellation token you would need to pass the token to every step in the
chain to both stop work being done and to ignore the result/prevent a
handler from being called. Wouldn't it be better if the promise chain took
care of this for the programmer?

Marius Gundersen
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-03 Thread Salvador de la Puente González
El 03/03/2015 22:42, Marius Gundersen gunder...@gmail.com escribió:



 On Tue, Mar 3, 2015 at 12:49 AM, Ron Buckton rbuck...@chronicles.org
wrote:

 ```js

 function doLater(callback, token) {

   setImmediate(() = {

 if (token.canceled) return;

 callback();

   });

 }

 ```


 But in this case, I still have a callback scheduled which may be
unnecessary overhead. If this were a native API, we could chose to
prioritize cancellation registrations over promise tasks. Even if the
registrations are async, the cancellation signal would still need to be
observable in a synchronous manner, even if only through reading the
CancellationToken#canceled property.

 Since cancellations are likely to be triggered asynchronously by the
user, it doesn't really matter if the cancellation signal is async or not,
it might still be too late to cancel the job. For example:

 ```js

 new Promise(resolve = doLater(resolve, cts.token)).then(handleResult);
 setImmediate(() = cts.cancel());
 ```
 In this scenario cancel would be called right after the resolve method is
called, but before handlerResult is called. For this to work with a
cancellation token you would need to pass the token to every step in the
chain to both stop work being done and to ignore the result/prevent a
handler from being called.

It is not necessary, if you don't care about the result as Dean pointed at
the beginning of the thread, and the promise is already resolved, let's be
it while if it is not resolved, the implementation could never call
acceptance or rejection callbacks and this way, the promise is effectively
prevented from calling any further callback.

Wouldn't it be better if the promise chain took care of this for the
programmer?

Cancellation is a feature of the value, not of the promise itself. The
value should follow a cancelable architecture. I.e. `fetch()` will resolve
almost immediately with a value not representing a `response` but a
`cancellableProcess` then it could offer a `.cancel()` method, a
`.isCancelled` flag and a `.response()` method which resolve in the actual
`response`, rejects if network error or never resolves if the flag is true.


 Marius Gundersen


 ___
 es-discuss mailing list
 es-discuss@mozilla.org
 https://mail.mozilla.org/listinfo/es-discuss

___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-02 Thread Andrea Giammarchi
);
 }
 }
 }

 var stopTicker = new CancellationTokenSource();
 stopTicker.cancelAfter(5 * 60 * 1000); // stop after 5 minutes.
 startTicker(..., stopTicker.token).catch(...); // .catch only gets error
 from `fetchSymbols`.


  Ron
  --
 *From:* es-discuss es-discuss-boun...@mozilla.org on behalf of Dean
 Tribble trib...@e-dean.com
 *Sent:* Monday, March 02, 2015 1:25 PM
 *To:* Kevin Smith
 *Cc:* public-script-co...@w3.org; es-discuss
 *Subject:* Re: Cancellation architectural observations

   On Mon, Mar 2, 2015 at 6:32 AM, Gray Zhang otakus...@icloud.com wrote:

  +1 to the ignore term, I’ve opened an issue about it in
 https://github.com/promises-aplus/cancellation-spec/issues/14

 I have little attachment to any term, but there's value in keeping
 terminology that has years of investment and use in other contexts. However
 ignore also has the wrong sense, because it implies that the computation
 completes anyway. That can be accomplished more easily by simply dropping
 the promise.

  IMO the term cancel(or abort) and ignore are totally different things,
 the former one means “do not continue, stop it right now” and the “stop”
 state should be broadcast to everyone who is interested in the work, while
 the latter means “I don’t care about the result anymore, just play it as
 you like”, it means the async progress can be continued

  This goes back to some of the observations above: you cannot stop it
 right now because async notification is not synchronous; indeed the
 operation may already be complete before you stop it. Thus consumers of the
 result of a cancellable request need to be able to handle either successful
 completion or the cancelled state (which just looks like any other error
 that prevented completion).  Attempting broadcast to everyone adds
 complexity and resources that are needed only in the rare cancellation
 case. It's typically not only not worth the software complexity, but not a
 good idea. When you cancel a print job, the document editor should make
 best efforts in the background to stop requesting fonts, stop laying out
 print pages, stop spitting out pages on the printer, etc. but most
 importantly, it should start paying attention to my new edits and hang
 waiting for everything that might be involved in printing to wrap itself up.

  In practice both scenario are commonly seen, we may abort a resource
 fetch in order to save bandwidth and opened connections, or we may in other
 side just ignore it since continue to complete the fetch can result in a
 local cache, which speeds up our fetch next time

 The resource point is important. That's the don't care scenario, not the
 abort scenario. It's the request processor that knows what cleanup is
 worth the effort. The initiator of the request only knows they don't care
 about the result anymore.

 ___
 es-discuss mailing list
 es-discuss@mozilla.org
 https://mail.mozilla.org/listinfo/es-discuss


___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


RE: Cancellation architectural observations

2015-03-02 Thread Domenic Denicola
From: es-discuss [mailto:es-discuss-boun...@mozilla.org] On Behalf Of Tab 
Atkins Jr.

 Cancellations should chain

This is the most important issue, in my mind. I often illustrate it with

```js
function fetchUser(id) {
  return fetch(`/users/user/${id}`);
}

function fetchUserAsJSON(id) {
  return fetchUser(id).then(JSON.parse);
}
```

If `fetchUser(1)` is cancellable but `fetchUserAsJSON(1)` is not, then I think 
we've failed.

However, I think this still works with cancellation tokens:

```js
function fetchUser(id, canceller) {
  return fetch(`/users/user/${id}`, { canceller });
}

function fetchUserAsJSON(id, canceller) {
  return fetchUser(id, canceller).then(JSON.parse);
}
```

The API stays the same for both of them. 

 Combinators should combine cancellations

Still works, I think, although again in a different form:

```js
function fetchUsers(ids, canceller) {
  return Promise.all(ids.map(id = fetchUser(id, canceller)));
}
```

 You need to be able to clean a cancellable promise

Simple:

```js
function fetchUserUncancellable(id) {
  return fetchUser(id); // no second argument
}
```

Overall I am more optimistic about cancellation tokens these days...
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-02 Thread Tab Atkins Jr.
Thanks for this summary of some concerns!  All valid, I think.

In the GitHub issue
https://github.com/slightlyoff/ServiceWorker/issues/625, there are
some additional usability concerns, which I think make the
cancellation token approach much less attractive, and lean the desired
solution more towards a promise subclass.  In particular:

Cancellations should chain
==
If you have a cancellable promise p1, and use .then() to produce a new
promise p2, p2 should also be cancelable, and in the default case,
should chain up to p1 and cause it to cancel as well.

If you chain multiple promises off of p1, like p2a and p2b, then
canceling either one of the p2X promises should do nothing, but
cancelling *both* of them should cancel p1. In other words, p1 can
ref-count its child promises that retain cancellation abilities, and
cancel itself when everything consuming its result has been cancelled.

This is important so you don't have to explicitly keep track of every
single cancelable thing you're doing, if you're only using it to
immediately chain onward again.  You can just care about the final
result, and if you end up not needing it, you can cancel it and it
walks back and tries to cancel everything your result depends on.

Combinators should combine cancellations
=

If you do `let fp3 = FetchPromise.all(fp1, fp2)`, then an fp3.cancel()
should try to cancel fp1 and fp2, as noted above.  You want all the
(cancellation-aware) combinators to be just as friendly as chaining
directly, for usability.

You need to be able to clean a cancellable promise


If the promise is what carries the cancellation ability, you need to
be able to observe its value without carrying the cancellability
around, to prevent spreading power around in an unwanted way (and
prevent upping the chained promises refcount).  This is doable by
just wrapping it in a standard promise - `Promise.resolve(fetch(...))`
will return a normal non-cancellable promise.


A cancellation token is basically an ocap, and that means you have to
keep track of the ocaps explicitly and separately from the promise for
the result.  This means more value-passing, and when you return
another cancellable promise in the callback (like
`fetch(...).then(x=fetch(...))`), you have to explicitly smuggle that
cancellation token out of the callback and hold onto both of them.
Combinators become annoying, as you have to grab *all* of the cancel
tokens used and hold them together, etc.

Attaching cancellation to the promise just provides more usable
behavior overall, without preventing safe behavior when you desire it.

~TJ
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-02 Thread Ron Buckton
While I agree that many specific use cases of abort could be this simple, 
more complex cases might be better satisfied by a separate abstraction. There 
are two issues I see with this approach:


  *   A layer that sits between the promise consumer and the promise producer 
can directly abort the promise.
  *   It is much more complicated to cancel large chunks of asynchronously 
executing code in bulk (such as when navigating to a new page, or shutting down 
an application cleanly).

I have examples of [1] and [2] here: 
https://gist.github.com/rbuckton/256c4e929f4a097e2c16


The gist basically contains four scenarios:

  *   Promise Producer/Cancellation Consumer
  *   Promise Consumer/Cancellation Producer
  *   Promise Consumer/Cancellation Consumer
  *   Cancellation Aggregation

I will adapt these somewhat to your sample here:


Promise Producer/Cancellation Consumer


```js

function fetch(url, config) {

  config || (config = {});

  return new Promise(function (res, rej) {

var xhr = new XMLHttpRequest();

xhr.addEventListener('error', function (pe) { rej(xhr); });

xhr.addEventListener('load', function (pe) { res(xhr); });

if (config.onabort)

  xhr.addEventListener('abort', config.onabort);

if (config.onprogress)

  xhr.addEventListener('progress', config.onprogress);

if (config.token) {

  config.token.register(function () { xhr.abort(); });

}

xhr.open(config.method || 'GET', url, true);

xhr.send(null);

  });

}

```


Promise Consumer/Cancellation Producer


```js

// abort

var cts = new CancellationTokenSource();

fetch('?page', { onabort: console.warn.bind(console), token: cts.token });

cts.cancel();


// with progress too

var cts = new CancellationTokenSource();

fetch('?page', { onabort: console.warn.bind(console), onprogress: 
console.log.bind(console), token: cts.token });

cts.cancel();

```


Promise Consumer/Cancellation Consumer


```js

function markLoading(value) {

  document.querySelector(#loading).style.display = value ? block : none;

}


function fetchPage(token) {

  markLoading(true);

  if (token)

token.register(function() { markLoading(false); });

  return fetch('?page', { token }).then(

value = {

  markLoading(false);

  return value;

},

reason = {

  markLoading(false);

  throw reason;

});
}

```


Cancellation Aggregation


```js

var root = new CancellationTokenSource();


function fetchPage(token) {

  token = token ? new CancellationTokenSource([root.token, token]).token : 
token;

  token.register(function() { markLoading(false); });

  return fetch('?page', { token }).then(

value = {

  markLoading(false);

  return value;

},

reason = {

  markLoading(false);

  throw reason;

});
}


// abort this request

var cts = new CancellationTokenSource();

fetchPage(cts.token);

cts.cancel();


// abort all requests

fetchPage();

fetchPage();

root.cancel();

```


Ron



From: Andrea Giammarchi andrea.giammar...@gmail.com
Sent: Monday, March 02, 2015 4:18 PM
To: Ron Buckton
Cc: Dean Tribble; Kevin Smith; public-script-co...@w3.org; es-discuss
Subject: Re: Cancellation architectural observations

So this is my simplified view of the matter ... it already works, and it aborts 
eventually with the ability to ignore the onabort callback.

The config object can have `onabort` that activates the abort-ability, the 
`onprogress` so that eventually this promise inside a generator can still 
update UIs, and potentially any other sort of property but for demo sake just 
`method` for GET, HEAD, PUT, POST and other requests.

```js
function fetch(url, config) {
  config || (config = {});
  var
xhr = new XMLHttpRequest,
promise = new Promise(function (res, rej) {
  xhr.addEventListener('error', function (pe) { rej(xhr); });
  xhr.addEventListener('load', function (pe) { res(xhr); });
  if (config.onabort)
xhr.addEventListener('abort', config.onabort);
  if (config.onprogress)
xhr.addEventListener('progress', config.onprogress);
  xhr.open(config.method || 'GET', url, true);
  xhr.send(null);
})
  ;
  if (config.onabort)
promise.abort = xhr.abort.bind(xhr);
  return promise;
}
```

abort example:
`fetch('?page', {onabort: console.warn.bind(console)}).abort();`

with progress too
`fetch('?page', {onabort: console.warn.bind(console), onprogress: 
console.log.bind(console)}).abort();`

full request
`fetch('?page', {onabort: console.warn.bind(console), onprogress: 
console.log.bind(console)}).then(console.log.bind(console));`

Why this code? Simply to somehow show that I am all for getting this right, but 
to me it's also probably a simpler matter than it looks like, specially for 
cases where cancel or abort is meant and needed.

Best Regards


On Mon, Mar 2, 2015 at 7:45 PM, Ron Buckton 
rbuck...@chronicles.orgmailto:rbuck...@chronicles.org wrote:

​

In light

Re: Cancellation architectural observations

2015-03-02 Thread Kevin Smith


 I'm not sure!  The mapping between promises and async functions isn't
 intuitive to me yet, and I'm not sure how async functions will be able
 to produce promise subclasses rather than plain promises.


Fair enough.  I think, though, that one of the design goals for a
cancellation architecture needs to be that we are able to created
cancelable tasks with async functions.

My intuition here is that async functions rely on the fact that promises
convey no information or capability other than what is represented by their
completion value.  That's what enables us to make the leap from promise API
to declarative syntax.
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-02 Thread Tab Atkins Jr.
On Mon, Mar 2, 2015 at 3:18 PM, Kevin Smith zenpars...@gmail.com wrote:
 I'm afraid I don't quite understand.  How is one supposed to create a
 cancelable task with async functions, under your proposed architecture?

I'm not sure!  The mapping between promises and async functions isn't
intuitive to me yet, and I'm not sure how async functions will be able
to produce promise subclasses rather than plain promises.

~TJ
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-02 Thread Kevin Smith
Hi Tab,

I'm afraid I don't quite understand.  How is one supposed to create a
cancelable task with async functions, under your proposed architecture?

On Mon, Mar 2, 2015 at 6:06 PM, Tab Atkins Jr. jackalm...@gmail.com wrote:

 Thanks for this summary of some concerns!  All valid, I think.

 In the GitHub issue
 https://github.com/slightlyoff/ServiceWorker/issues/625, there are
 some additional usability concerns, which I think make the
 cancellation token approach much less attractive, and lean the desired
 solution more towards a promise subclass.  In particular:

 Cancellations should chain
 ==
 If you have a cancellable promise p1, and use .then() to produce a new
 promise p2, p2 should also be cancelable, and in the default case,
 should chain up to p1 and cause it to cancel as well.

 If you chain multiple promises off of p1, like p2a and p2b, then
 canceling either one of the p2X promises should do nothing, but
 cancelling *both* of them should cancel p1. In other words, p1 can
 ref-count its child promises that retain cancellation abilities, and
 cancel itself when everything consuming its result has been cancelled.

 This is important so you don't have to explicitly keep track of every
 single cancelable thing you're doing, if you're only using it to
 immediately chain onward again.  You can just care about the final
 result, and if you end up not needing it, you can cancel it and it
 walks back and tries to cancel everything your result depends on.

 Combinators should combine cancellations
 =

 If you do `let fp3 = FetchPromise.all(fp1, fp2)`, then an fp3.cancel()
 should try to cancel fp1 and fp2, as noted above.  You want all the
 (cancellation-aware) combinators to be just as friendly as chaining
 directly, for usability.

 You need to be able to clean a cancellable promise
 

 If the promise is what carries the cancellation ability, you need to
 be able to observe its value without carrying the cancellability
 around, to prevent spreading power around in an unwanted way (and
 prevent upping the chained promises refcount).  This is doable by
 just wrapping it in a standard promise - `Promise.resolve(fetch(...))`
 will return a normal non-cancellable promise.


 A cancellation token is basically an ocap, and that means you have to
 keep track of the ocaps explicitly and separately from the promise for
 the result.  This means more value-passing, and when you return
 another cancellable promise in the callback (like
 `fetch(...).then(x=fetch(...))`), you have to explicitly smuggle that
 cancellation token out of the callback and hold onto both of them.
 Combinators become annoying, as you have to grab *all* of the cancel
 tokens used and hold them together, etc.

 Attaching cancellation to the promise just provides more usable
 behavior overall, without preventing safe behavior when you desire it.

 ~TJ
 ___
 es-discuss mailing list
 es-discuss@mozilla.org
 https://mail.mozilla.org/listinfo/es-discuss

___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-02 Thread Ron Buckton
The upside of having a separate abstraction for cancellation, is that it 
composes well with async functions:

```js
async function runStockTicker(receiveSymbol, cancellationToken) {
  while (!cancellationToken.canceled) {
var symbols = await fetchSymbols(cancellationToken);
if (!cancellationToken.canceled) {
  for (var symbol of symbols) {
receiveSymbol(symbol);
  }
  await sleep(1000);
}
  }
}

var stopTicker = new CancellationTokenSource();
runStockTicker(..., stopTicker.token);
...
stopTicker.cancel(); // stop the current fetch and the loop
```

Ron

From: es-discuss es-discuss-boun...@mozilla.org on behalf of Tab Atkins Jr. 
jackalm...@gmail.com
Sent: Monday, March 02, 2015 6:21 PM
To: Kevin Smith
Cc: public-script-co...@w3.org; Dean Tribble; es-discuss
Subject: Re: Cancellation architectural observations

On Mon, Mar 2, 2015 at 3:18 PM, Kevin Smith zenpars...@gmail.com wrote:
 I'm afraid I don't quite understand.  How is one supposed to create a
 cancelable task with async functions, under your proposed architecture?

I'm not sure!  The mapping between promises and async functions isn't
intuitive to me yet, and I'm not sure how async functions will be able
to produce promise subclasses rather than plain promises.

~TJ
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-02 Thread Ron Buckton
 Cancellations should chain
 ==
 If you have a cancellable promise p1, and use .then() to produce a new
 promise p2, p2 should also be cancelable, and in the default case,
 should chain up to p1 and cause it to cancel as well.

The CTS/Token approach can do this directly without needing a chain, which is 
observable on the p2 via its onrejected handler.

 If you chain multiple promises off of p1, like p2a and p2b, then
 canceling either one of the p2X promises should do nothing, but
 cancelling *both* of them should cancel p1. In other words, p1 can
 ref-count its child promises that retain cancellation abilities, and
 cancel itself when everything consuming its result has been cancelled.

 This is important so you don't have to explicitly keep track of every
 single cancelable thing you're doing, if you're only using it to
 immediately chain onward again.  You can just care about the final
 result, and if you end up not needing it, you can cancel it and it
 walks back and tries to cancel everything your result depends on.

CTS partially accomplishes this through linked registrations, though assuming 
that if all chained promises are canceled that the root should be canceled 
could be a mistake:

```js
var rootCts = new CancellationTokenSource();
var configPromise = fetchConfig(rootCts.token);

var alphaCts = new CancellationTokenSource();
var alphaPromise = fetchConfigProperty(configPromise, alpha, alphaCts.token);

var betaCts = new CancellationTokenSource();
var betaPromise = fetchConfigProperty(configPromise, beta, betaCts.token);

alphaCts.cancel();
betaCts.cancel();

// do we want to cancel configPromise here? What if we want to enlist via .then 
later?
// if we are sure we want that behavior we can do this explicitly

var requests = new Set();
function fetchConfigProperty(configPromise, name, token) {
  requests.add(token);
  var registration = token.register(() = {
requests.delete(token);
if (requests.size = 0) { rootCts.cancel(); }
  });
  return configPromise.then(
config = {
  requests.delete(token);
  registration.unregister();
  return config[name];
},
reason = {
  requests.delete(token);
  registration.unregister();
  throw reason;
});
}
```

 Combinators should combine cancellations
 =

 If you do `let fp3 = FetchPromise.all(fp1, fp2)`, then an fp3.cancel()
 should try to cancel fp1 and fp2, as noted above.  You want all the
 (cancellation-aware) combinators to be just as friendly as chaining
 directly, for usability.

If the fetch api supports tokens, you could instead do:

```js
var cts = new CancellationTokenSource();
var fp1 = fetch(..., cts.token);
var fp2 = fetch(..., cts.token);
let fp3 = Promise.all([fp1, fp2]);
cts.cancel(); // will cancel fp1 and fp2
```

 You need to be able to clean a cancellable promise
 

 If the promise is what carries the cancellation ability, you need to
 be able to observe its value without carrying the cancellability
 around, to prevent spreading power around in an unwanted way (and
 prevent upping the chained promises refcount).  This is doable by
 just wrapping it in a standard promise - `Promise.resolve(fetch(...))`
 will return a normal non-cancellable promise.

This behavior is an explicit benefit of CTS, as it does not conflate Promise 
with the cancellation signal, and is very explicit about the source of 
cancellation. If you want to expose a promise to a consumer without 
cancellation, and instead support cancellation internally, you would do the 
following:

```js
// [api.js]
var rootCts = new CancellationTokenSource();
export function getRecord(id) {
  // cancellation not exposed to consumer
  return fetch(id, rootCts.token);
}
export function shutdown() {
  rootCts.cancel(); // cancellation only exposed to consumer via explicit entry 
point
}

// [consumer.js]
import { getRecord, shutdown } from 'consumer';
var fp1 = getRecord(...); // cannot cancel this individual request
var fp2 = getRecord(...);
shutdown(); // cancels all requests through explicit entry point
```

 A cancellation token is basically an ocap, and that means you have to
 keep track of the ocaps explicitly and separately from the promise for
 the result.  This means more value-passing, and when you return
 another cancellable promise in the callback (like
 `fetch(...).then(x=fetch(...))`), you have to explicitly smuggle that
 cancellation token out of the callback and hold onto both of them.
 Combinators become annoying, as you have to grab *all* of the cancel
 tokens used and hold them together, etc.

That's what linking registrations are for, and you should only need to add 
cancellation to methods where you can explicitly cancel. You wouldn't need 
tokens for every single promise. Also, the token doesn't come out of the 
callback, but rather is passed in from the caller:

```
var cts = new CancellationTokenSource();
var f = token = 

Re: Cancellation architectural observations

2015-03-02 Thread Kevin Smith

 *Cancellation*

- Cancellation signals are *produced* by the caller and * consumed* by
the callee.
- Observation a cancellation signal must happen *immediately*.

 This is a crucially important question.  Can you elaborate on why you
think this is the case?
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-02 Thread Jonas Sicking
On Sun, Mar 1, 2015 at 11:06 PM, Dean Tribble trib...@e-dean.com wrote:

 Cancel requests, not results
 Promises are like object references for async; any particular promise might
 be returned or passed to more than one client. Usually, programmers would be
 surprised if a returned or passed in reference just got ripped out from
 under them by another client. this is especially obvious when considering a
 library that gets a promise passed into it. Using cancel on the promise is
 like having delete on object references; it's dangerous to use, and
 unreliable to have used by others.

I do wonder if there are two types of cancelling. Which I think
several of the posts in this thread has touched on.

On one hand there are operations like read requests from the
filesystem, side-effect free GET requests from the network, and
computation requests such as calculating the SHA-256 value of a large
document.

In all of these instances the operation has no side effects other than
producing the value that the promise resolves to.


The second category is operations that do have side effects. Such as a
writing operation to a filesystem or a database, or a POST network
request.

(For now ignoring the fact that some GET requests do have side
effects, and some POST requests don't).


The second category seems simpler. Here it's clear that a lack of
interest in the result does not mean that any requests can or should
be cancelled. An approach like a cancellation token definitely seems
sensible here.


For the first category, a lack of interest in the result means that we
should abort all requests involved in producing that result.

For stream-like APIs we've sort of taken this for granted. Once
back-pressure, or a call to .close() (or similar), indicates that data
is currently not being consumed, the API automatically signals to all
producers that they can and should stop producing results.

Why should we treat APIs that return a single value, rather than a
stream of values, any differently?

But it requires knowing that *no one* is interested in the result.
Simply having access to a Promise object does not guarantee that due
to Promises nature that you can not only call .then() multiple times,
the time when calling .then() is permitted is unbounded.

So I think I'm persuaded by the argument that as long as an API
returns a Promise, there is no way that we can put a
cancellation/result-ignoring/close() API on that Promise. It seems to
simply break the Promise contract.

Instead we'd need to return some object which allows us to track
consumers. I.e. where if you call a .then-like function, you prevent
anyone else from doing so. And if you want to enable others to consume
the result, you have to first fork the result and then call the
.then-like function on your fork.

Unfortunately that .then-like function probably can't be called then
given the elevated status that Promises has given that function name.

/ Jonas
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-02 Thread Tab Atkins Jr.
On Mon, Mar 2, 2015 at 3:39 PM, Ron Buckton rbuck...@chronicles.org wrote:
 CTS partially accomplishes this through linked registrations, though assuming 
 that if all chained promises are canceled that the root should be canceled 
 could be a mistake:

 ```js
 var rootCts = new CancellationTokenSource();
 var configPromise = fetchConfig(rootCts.token);

 var alphaCts = new CancellationTokenSource();
 var alphaPromise = fetchConfigProperty(configPromise, alpha, 
 alphaCts.token);

 var betaCts = new CancellationTokenSource();
 var betaPromise = fetchConfigProperty(configPromise, beta, betaCts.token);

 alphaCts.cancel();
 betaCts.cancel();

 // do we want to cancel configPromise here? What if we want to enlist via 
 .then later?

Yes, you do want configPromise to cancel there.  It's the common case.
If you dont' want that to happen, produce all your chained promises
*first*; if you can't do that, chain a dummy promise off of it (to
increase the ref count) and then cancel it when you're done producing
chained promises.  That'll ensure configPromise stays alive until
you're done chaining things off of it.

 Combinators should combine cancellations
 =

 If you do `let fp3 = FetchPromise.all(fp1, fp2)`, then an fp3.cancel()
 should try to cancel fp1 and fp2, as noted above.  You want all the
 (cancellation-aware) combinators to be just as friendly as chaining
 directly, for usability.

 If the fetch api supports tokens, you could instead do:

 ```js
 var cts = new CancellationTokenSource();
 var fp1 = fetch(..., cts.token);
 var fp2 = fetch(..., cts.token);
 let fp3 = Promise.all([fp1, fp2]);
 cts.cancel(); // will cancel fp1 and fp2
 ```

That works if you produced the source cancelables, and had the ability
to initialize them with the same cancel token.  If you didn't, and had
their individual cancel tokens passed in a side channel, it's much
more frustrating, as you have to explicitly track all of their tokens
together and then cancel all of them.

This doesn't seem complicated when you're just looking at simple
examples like this, but it's extra information that quickly gets out
of hand when you're working on real-world code.

 This behavior is an explicit benefit of CTS, as it does not conflate Promise 
 with the cancellation signal, and is very explicit about the source of 
 cancellation. If you want to expose a promise to a consumer without 
 cancellation, and instead support cancellation internally, you would do the 
 following:

Yeah, you just don't hand the consumer the cancellation token, that's easy.

 That's what linking registrations are for, and you should only need to add 
 cancellation to methods where you can explicitly cancel. You wouldn't need 
 tokens for every single promise. Also, the token doesn't come out of the 
 callback, but rather is passed in from the caller:

Sorry, I didn't provide an example, so you misunderstand what I'm
referring to.  I mean in the case of something like:

```
let p = somePromiseFunc().then(x=fetch(...));
```

If you want that fetch to be cancellable, you need to explicitly
create a token outside to pass in:

```
let ct = new CancelToken();
let p = somePromiseFunc().then(x=fetch(..., ct));
```

Which, again, doesn't seem like any big burden when you're looking at
trivial isolated examples, but is that much more state you have to
carry around for every single fetch() you do.  Imagine you were
kicking off a variable number of fetches, for example - then you need
to create an array on the outside, and create your cancel tokens on
the inside and push them into the array to exfiltrate them.  This kind
of exfiltration is annoying when you're trying to extract the
resolve/reject functions from the Promise constructor, but that's
fairly rare; requiring it for common cases of fetch() usage makes it
obnoxious.

Whereas with a promise subclass, you do have to know that you want it
to be cancellable, but then you write:

```
let p = FetchPromise.resolve(somePromiseFunc()).then(x=fetch(...));
```

You need to upgrade the vanilla promise into a cancellable promise,
but then chaining will work - if you cancel p, it'll cancel the
fetch() too, without having to keep track of anything further.

 Adding cancellation directly to a Promise means that supporting safe behavior 
 is harder than the default.

Agreed, and it's not ideal, but it's not an automatic tradeoff.  If
the safe behavior is significantly less usable, it can still be worth
it to default to unsafe, as long as switching to safe is fairly easy
(and it is).

~TJ
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-02 Thread John Lenz
This summaries my concerns and attitude toward this issue very well, thank
you.

On Mon, Mar 2, 2015 at 3:06 PM, Tab Atkins Jr. jackalm...@gmail.com wrote:

 Thanks for this summary of some concerns!  All valid, I think.

 In the GitHub issue
 https://github.com/slightlyoff/ServiceWorker/issues/625, there are
 some additional usability concerns, which I think make the
 cancellation token approach much less attractive, and lean the desired
 solution more towards a promise subclass.  In particular:

 Cancellations should chain
 ==
 If you have a cancellable promise p1, and use .then() to produce a new
 promise p2, p2 should also be cancelable, and in the default case,
 should chain up to p1 and cause it to cancel as well.

 If you chain multiple promises off of p1, like p2a and p2b, then
 canceling either one of the p2X promises should do nothing, but
 cancelling *both* of them should cancel p1. In other words, p1 can
 ref-count its child promises that retain cancellation abilities, and
 cancel itself when everything consuming its result has been cancelled.

 This is important so you don't have to explicitly keep track of every
 single cancelable thing you're doing, if you're only using it to
 immediately chain onward again.  You can just care about the final
 result, and if you end up not needing it, you can cancel it and it
 walks back and tries to cancel everything your result depends on.

 Combinators should combine cancellations
 =

 If you do `let fp3 = FetchPromise.all(fp1, fp2)`, then an fp3.cancel()
 should try to cancel fp1 and fp2, as noted above.  You want all the
 (cancellation-aware) combinators to be just as friendly as chaining
 directly, for usability.

 You need to be able to clean a cancellable promise
 

 If the promise is what carries the cancellation ability, you need to
 be able to observe its value without carrying the cancellability
 around, to prevent spreading power around in an unwanted way (and
 prevent upping the chained promises refcount).  This is doable by
 just wrapping it in a standard promise - `Promise.resolve(fetch(...))`
 will return a normal non-cancellable promise.


 A cancellation token is basically an ocap, and that means you have to
 keep track of the ocaps explicitly and separately from the promise for
 the result.  This means more value-passing, and when you return
 another cancellable promise in the callback (like
 `fetch(...).then(x=fetch(...))`), you have to explicitly smuggle that
 cancellation token out of the callback and hold onto both of them.
 Combinators become annoying, as you have to grab *all* of the cancel
 tokens used and hold them together, etc.

 Attaching cancellation to the promise just provides more usable
 behavior overall, without preventing safe behavior when you desire it.

 ~TJ
 ___
 es-discuss mailing list
 es-discuss@mozilla.org
 https://mail.mozilla.org/listinfo/es-discuss

___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-02 Thread Ron Buckton
The simplest example is:


```js

function doLater(callback, token) {

  var handle = setImmediate(callback);

  token.register(() = clearImmediate(handle));
}


var cts = new CancellationTokenSource();

doLater(..., cts.token);

cts.cancel();

```


If token registrations occur in a later turn, I have no way to clear the 
handle. I can write the following instead, however:


```js

function doLater(callback, token) {

  setImmediate(() = {

if (token.canceled) return;

callback();

  });

}

```


But in this case, I still have a callback scheduled which may be unnecessary 
overhead. If this were a native API, we could chose to prioritize cancellation 
registrations over promise tasks. Even if the registrations are async, the 
cancellation signal would still need to be observable in a synchronous manner, 
even if only through reading the CancellationToken#canceled property.


Ron



From: Kevin Smith zenpars...@gmail.com
Sent: Monday, March 02, 2015 6:42 PM
To: Ron Buckton
Cc: public-script-co...@w3.org; es-discuss
Subject: Re: Cancellation architectural observations


Cancellation

  *   Cancellation signals are produced by the caller and consumed by the 
callee.
  *   Observation a cancellation signal must happen immediately.

This is a crucially important question.  Can you elaborate on why you think 
this is the case?

___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-02 Thread Tab Atkins Jr.
On Mon, Mar 2, 2015 at 3:52 PM, Jonas Sicking jo...@sicking.cc wrote:
 So I think I'm persuaded by the argument that as long as an API
 returns a Promise, there is no way that we can put a
 cancellation/result-ignoring/close() API on that Promise. It seems to
 simply break the Promise contract.

 Instead we'd need to return some object which allows us to track
 consumers. I.e. where if you call a .then-like function, you prevent
 anyone else from doing so. And if you want to enable others to consume
 the result, you have to first fork the result and then call the
 .then-like function on your fork.

 Unfortunately that .then-like function probably can't be called then
 given the elevated status that Promises has given that function name.

This was the approach I was angling for early in the FetchPromise
thread at 
https://github.com/slightlyoff/ServiceWorker/issues/625#issuecomment-75302002;
I originally proposed that .then() always return a vanilla Promise,
and a separate method (I proposed .pipe()) do a chain that maintained
the cancellability (triggering refcounting and such).

~TJ
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-02 Thread Gray Zhang
+1 to the ignore term, I’ve opened an issue about it in 
https://github.com/promises-aplus/cancellation-spec/issues/14

IMO the term cancel(or abort) and ignore are totally different things, the 
former one means “do not continue, stop it right now” and the “stop” state 
should be broadcast to everyone who is interested in the work, while the latter 
means “I don’t care about the result anymore, just play it as you like”, it 
means the async progress can be continued

In practice both scenario are commonly seen, we may abort a resource fetch in 
order to save bandwidth and opened connections, or we may in other side just 
ignore it since continue to complete the fetch can result in a local cache, 
which speeds up our fetch next time

Back to the ignore scenario, there is still some confusion left for me:

Should ignore be a individual behavior, or a global one. This means should we 
just be able to “ignore” one callback, or just take a promise to a “ignore” 
state in which all callbacks are revoked
Should later then calls be aware that the promise is already ignored, or 
ignorance should be completely transparent to clients (which means all later 
then are effortless)


Best regards

Gray Zhang



在 2015年3月2日 下午7:26:52, Gundersen Marius (gunder...@gmail.com) 写到:

I think a better term than cancel would be ignore. If the promise has side 
effects then it is impossible for a cancel method to guarantee that the side 
effects do not happen. For example a POST request to the server that updates 
the server state can be cancelled, but it is not possible to know if it happens 
before or after the server has updated its state. It is therefore dangerous to 
think of it as cancelling. A better concept is to ignore the results of the 
promise. Even if the promise has been completed, the results can be ignored. 
Depending on what job the promise is doing it can decide if it wants to stop 
the task or not. A simple async task might not need to have a way to be 
cancelled; the result of the task can just be ignored. But a long running task 
that is ignored might decide to stop what it is doing to save processing 
power/battery.

```js
//this promise has a way to stop what it is doing
var p1 = new Promise(function(resolve, reject, ignored){
  var task = setTimeout(resolve.bind(null, the result), 1000);
  ignored(() = clearTimeout(task));
});

//ignore the result from the above promise, triggers the ignored listener
p1.ignore();
p1.then(result = this is never called);

//this promise cannot be cancelled, but the result can be ignored
var p2 = Promise.resolve(the result);

//the callback is not called until next event loop cycle
p2.then(result = this is never called);

//the result is ignored, the above callback is never run
p2.ignore();
```

Maybe calling ignore on a rejected or resolved promise should throw.

Marius Gundersen

On Mon, Mar 2, 2015 at 8:59 AM, Salvador de la Puente González 
sa...@unoyunodiez.com wrote:
I think this did not reach the mailing list to the W3:
https://esdiscuss.org/topic/cancelable-promises#content-9

Actually the result cancellation is different from promise cancellation. First 
is full of implementation details, thus you are talking about smart 
cancellation. It is not smart, is the only way to provide control, thus, the 
cancellationToken.

In the other side you're talking about don't care what is actually related 
with the flow control. It turns out, after a timeout, you are no longer 
interested in the result so you don't use it because you don't care.

I insist, two concepts: one for cancelling a control flow, another for an 
specific operation of the implementation.

El 02/03/2015 08:06, Dean Tribble trib...@e-dean.com escribió:
Another thread here brought up the challenge of supporting cancellation in an 
async environment. I spent some time on that particular challenge a few years 
ago, and it turned out to be bigger and more interesting than it appeared on 
the surface. In the another thread, Ron Buckton pointed at the .Net approach 
and it's use in JavaScript:
 
AsyncJS (http://github.com/rbuckton/asyncjs) uses a separate abstraction for 
cancellation based on the .NET CancellationTokenSource/CancellationToken types. 
You can find more information about this abstraction in the MSDN documentation 
here: https://msdn.microsoft.com/en-us/library/dd997364(v=vs.110).aspx

It's great that asyncjs already has started using it. I was surprised at how 
well the cancellationToken approach worked in both small applications and when 
extended to a very large async system. I'll summarize some of the architectural 
observations, especially from extending it to async:

Cancel requests, not results
Promises are like object references for async; any particular promise might be 
returned or passed to more than one client. Usually, programmers would be 
surprised if a returned or passed in reference just got ripped out from under 
them by another client. this is especially obvious when considering a 

Re: Cancellation architectural observations

2015-03-02 Thread Marius Gundersen
I think a better term than cancel would be ignore. If the promise has side
effects then it is impossible for a cancel method to guarantee that the
side effects do not happen. For example a POST request to the server that
updates the server state can be cancelled, but it is not possible to know
if it happens before or after the server has updated its state. It is
therefore dangerous to think of it as cancelling. A better concept is to
ignore the results of the promise. Even if the promise has been completed,
the results can be ignored. Depending on what job the promise is doing it
can decide if it wants to stop the task or not. A simple async task might
not need to have a way to be cancelled; the result of the task can just be
ignored. But a long running task that is ignored might decide to stop what
it is doing to save processing power/battery.

```js
//this promise has a way to stop what it is doing
var p1 = new Promise(function(resolve, reject, ignored){
  var task = setTimeout(resolve.bind(null, the result), 1000);
  ignored(() = clearTimeout(task));
});

//ignore the result from the above promise, triggers the ignored listener
p1.ignore();
p1.then(result = this is never called);

//this promise cannot be cancelled, but the result can be ignored
var p2 = Promise.resolve(the result);

//the callback is not called until next event loop cycle
p2.then(result = this is never called);

//the result is ignored, the above callback is never run
p2.ignore();
```

Maybe calling ignore on a rejected or resolved promise should throw.

Marius Gundersen

On Mon, Mar 2, 2015 at 8:59 AM, Salvador de la Puente González 
sa...@unoyunodiez.com wrote:

 I think this did not reach the mailing list to the W3:
 https://esdiscuss.org/topic/cancelable-promises#content-9

 Actually the result cancellation is different from promise cancellation.
 First is full of implementation details, thus you are talking about smart
 cancellation. It is not smart, is the only way to provide control, thus,
 the cancellationToken.

 In the other side you're talking about don't care what is actually
 related with the flow control. It turns out, after a timeout, you are no
 longer interested in the result so you don't use it because you don't care.

 I insist, two concepts: one for cancelling a control flow, another for an
 specific operation of the implementation.
 El 02/03/2015 08:06, Dean Tribble trib...@e-dean.com escribió:

 Another thread here brought up the challenge of supporting cancellation
 in an async environment. I spent some time on that particular challenge a
 few years ago, and it turned out to be bigger and more interesting than it
 appeared on the surface. In the another thread, Ron Buckton pointed at the
 .Net approach and it's use in JavaScript:


 AsyncJS (http://github.com/rbuckton/asyncjs) uses a separate
 abstraction for cancellation based on the .NET
 CancellationTokenSource/CancellationToken types. You can find more
 information about this abstraction in the MSDN documentation here:
 https://msdn.microsoft.com/en-us/library/dd997364(v=vs.110).aspx


 It's great that asyncjs already has started using it. I was surprised at
 how well the cancellationToken approach worked in both small applications
 and when extended to a very large async system. I'll summarize some of the
 architectural observations, especially from extending it to async:

 *Cancel requests, not results*
 Promises are like object references for async; any particular promise
 might be returned or passed to more than one client. Usually, programmers
 would be surprised if a returned or passed in reference just got ripped out
 from under them *by another client*. this is especially obvious when
 considering a library that gets a promise passed into it. Using cancel on
 the promise is like having delete on object references; it's dangerous to
 use, and unreliable to have used by others.

 *Cancellation is heterogeneous*
 It can be misleading to think about canceling a single activity. In most
 systems, when cancellation happens, many unrelated tasks may need to be
 cancelled for the same reason. For example, if a user hits a stop button on
 a large incremental query after they see the first few results, what should
 happen?

- the async fetch of more query results should be terminated and the
connection closed
- background computation to process the remote results into
renderable form should be stopped
- rendering of not-yet rendered content should be stopped. this might
include retrieval of secondary content for the items no longer of interest
(e.g., album covers for the songs found by a complicated content search)
- the animation of loading more should be stopped, and should be
replaced with user cancelled
- etc.

 Some of these are different levels of abstraction, and for any
 non-trivial application, there isn't a single piece of code that can know
 to terminate all these activities. This kind of system also requires 

Re: Cancellation architectural observations

2015-03-02 Thread Kevin Smith

 //this promise has a way to stop what it is doing
 var p1 = new Promise(function(resolve, reject, ignored){
   var task = setTimeout(resolve.bind(null, the result), 1000);
   ignored(() = clearTimeout(task));
 });


Wouldn't the cancellation token strategy be more appropriate here, for all
the reasons that Dean mentioned?  Something like:

function delay(ms, { cancellation } = {}) {
return new Promise(resolve = {
let id = setTimeout(_= resolve(), ms);
if (cancellation) cancellation.then(_= clearTimeout(id));
});
}

(Thanks, Dean, for taking the time to write that up BTW.)

//this promise cannot be cancelled, but the result can be ignored
 var p2 = Promise.resolve(the result);

 //the callback is not called until next event loop cycle
 p2.then(result = this is never called);

 //the result is ignored, the above callback is never run
 p2.ignore();


It seems like what you want here is to unregister the callback, similar
to how you would unregister an event listener.  It seems like that only
makes sense on a callback-by-callback basis, since other callbacks for the
same promise might still want to fire.  Can you not handle this use case
with existing API, plus higher order functions?

let cbs = forgettable();
p2.then(cbs(result = this is never called));
cbs.forget();
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Cancellation architectural observations

2015-03-02 Thread Andrea Giammarchi
 the rare cancellation case

abort is not a rare case, and an ignore that does not abort makes no sense
whatsoever.

If I am paying data and I've requested 10 tiles on a mobile maps based
sites and I search/go/locate somewhere else on the other edge on the world
it makes no sense to wait for the initial 10 tiles to downloads: either
because my cache won't hold them anyway due settings limitations or browser
behavior, or because I won't see the new location until new tiles requests
are queued behind old tiles.

We can drop downloads on XHR, there is a whatwg Fetch API proposal that is
based on network requests and AFAIK it does not provide a way to be
canceled (being Promise based)

A mechanism that tries to abort, instead of faking it, allows a faking
(read: ignore) logic to be implemented regardless, but not the other way
round.

Moreover, being sure about the end of the Network operation is a real-world
problem but not solvable here for the simple reason that my mobile network
could die anytime in the process, with or without ignored Promises.

So that maye my rejected error was triggered after the server already
updated ... how are we going to solve this anyway?

If the POST didn't explicitly complete, we could let the server know about
it with a lighter request, instead of keep sendong 25MB of image that you
realized is the wrong one ...
So, having real reactions on cancel/abort should be the only option, IMO,
so that we can solve explicit developers intent.

As summary: as a developer, I don't know yet which way is the best one, but
if there's a way to interrupt the execution, however it's going to be
called, I am expecting such explicit action to be done, and not to be
silently ignored, specially from the underlying logic ( network requests,
POST requests, download, preload, etc )

Please let's get this right, thank you.




On Mon, Mar 2, 2015 at 6:25 PM, Dean Tribble trib...@e-dean.com wrote:

 On Mon, Mar 2, 2015 at 6:32 AM, Gray Zhang otakus...@icloud.com wrote:

 +1 to the ignore term, I’ve opened an issue about it in
 https://github.com/promises-aplus/cancellation-spec/issues/14

 I have little attachment to any term, but there's value in keeping
 terminology that has years of investment and use in other contexts. However
 ignore also has the wrong sense, because it implies that the computation
 completes anyway. That can be accomplished more easily by simply dropping
 the promise.

 IMO the term cancel(or abort) and ignore are totally different things,
 the former one means “do not continue, stop it right now” and the “stop”
 state should be broadcast to everyone who is interested in the work, while
 the latter means “I don’t care about the result anymore, just play it as
 you like”, it means the async progress can be continued

  This goes back to some of the observations above: you cannot stop it
 right now because async notification is not synchronous; indeed the
 operation may already be complete before you stop it. Thus consumers of the
 result of a cancellable request need to be able to handle either successful
 completion or the cancelled state (which just looks like any other error
 that prevented completion).  Attempting broadcast to everyone adds
 complexity and resources that are needed only in the rare cancellation
 case. It's typically not only not worth the software complexity, but not a
 good idea. When you cancel a print job, the document editor should make
 best efforts in the background to stop requesting fonts, stop laying out
 print pages, stop spitting out pages on the printer, etc. but most
 importantly, it should start paying attention to my new edits and hang
 waiting for everything that might be involved in printing to wrap itself up.

 In practice both scenario are commonly seen, we may abort a resource
 fetch in order to save bandwidth and opened connections, or we may in other
 side just ignore it since continue to complete the fetch can result in a
 local cache, which speeds up our fetch next time

 The resource point is important. That's the don't care scenario, not the
 abort scenario. It's the request processor that knows what cleanup is
 worth the effort. The initiator of the request only knows they don't care
 about the result anymore.

 ___
 es-discuss mailing list
 es-discuss@mozilla.org
 https://mail.mozilla.org/listinfo/es-discuss


___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Cancellation architectural observations

2015-03-01 Thread Dean Tribble
Another thread here brought up the challenge of supporting cancellation in
an async environment. I spent some time on that particular challenge a few
years ago, and it turned out to be bigger and more interesting than it
appeared on the surface. In the another thread, Ron Buckton pointed at the
.Net approach and it's use in JavaScript:


 AsyncJS (http://github.com/rbuckton/asyncjs) uses a separate abstraction
 for cancellation based on the .NET
 CancellationTokenSource/CancellationToken types. You can find more
 information about this abstraction in the MSDN documentation here:
 https://msdn.microsoft.com/en-us/library/dd997364(v=vs.110).aspx


It's great that asyncjs already has started using it. I was surprised at
how well the cancellationToken approach worked in both small applications
and when extended to a very large async system. I'll summarize some of the
architectural observations, especially from extending it to async:

*Cancel requests, not results*
Promises are like object references for async; any particular promise might
be returned or passed to more than one client. Usually, programmers would
be surprised if a returned or passed in reference just got ripped out from
under them *by another client*. this is especially obvious when considering
a library that gets a promise passed into it. Using cancel on the promise
is like having delete on object references; it's dangerous to use, and
unreliable to have used by others.

*Cancellation is heterogeneous*
It can be misleading to think about canceling a single activity. In most
systems, when cancellation happens, many unrelated tasks may need to be
cancelled for the same reason. For example, if a user hits a stop button on
a large incremental query after they see the first few results, what should
happen?

   - the async fetch of more query results should be terminated and the
   connection closed
   - background computation to process the remote results into renderable
   form should be stopped
   - rendering of not-yet rendered content should be stopped. this might
   include retrieval of secondary content for the items no longer of interest
   (e.g., album covers for the songs found by a complicated content search)
   - the animation of loading more should be stopped, and should be
   replaced with user cancelled
   - etc.

Some of these are different levels of abstraction, and for any non-trivial
application, there isn't a single piece of code that can know to terminate
all these activities. This kind of system also requires that cancellation
support is consistent across many very different types of components. But
if each activity takes a cancellationToken, in the above example, they just
get passed the one that would be cancelled if the user hits stop and the
right thing happens.

*Cancellation should be smart*
Libraries can and should be smart about how they cancel. In the case of an
async query, once the result of a query from the server has come back, it
may make sense to finish parsing and caching it rather than just
reflexively discarding it. In the case of a brokerage system, for example,
the round trip to the servers to get recent data is the expensive part.
Once that's been kicked off and a result is coming back, having it
available in a local cache in case the user asks again is efficient. If the
application spawned another worker, it may be more efficient to let the
worker complete (so that you can reuse it) rather than abruptly terminate
it (requiring discarding of the running worker and cached state).

*Cancellation is a race*
In an async system, new activities may be getting continuously scheduled by
asks that are themselves scheduled but not currently running. The act of
cancelling needs to run in this environment. When cancel starts, you can
think of it as a signal racing out to catch up with all the computations
launched to achieve the now-cancelled objective. Some of those may choose
to complete (see the caching example above). Some may potentially keep
launching more work before that work itself gets signaled (yeah it's a bug
but people write buggy code). In an async system, cancellation is not
prompt. Thus, it's infeasible to ask has cancellation finished? because
that's not a well defined state. Indeed, there can be code scheduled that
should and does not get cancelled (e.g., the result processor for a pub/sub
system), but that schedules work that will be cancelled (parse the
publication of an update to the now-cancelled query).

*Cancellation is don't care*
Because smart cancellation sometimes doesn't stop anything and in an async
environment, cancellation is racing with progress, it is at most best
efforts. When a set of computations are cancelled, the party canceling the
activities is saying I no longer care whether this completes. That is
importantly different from saying I want to prevent this from completing.
The former is broadly usable resource reduction. The latter is only
usefully achieved in systems with 

Re: Cancellation architectural observations

2015-03-01 Thread Salvador de la Puente González
I think this did not reach the mailing list to the W3:
https://esdiscuss.org/topic/cancelable-promises#content-9

Actually the result cancellation is different from promise cancellation.
First is full of implementation details, thus you are talking about smart
cancellation. It is not smart, is the only way to provide control, thus,
the cancellationToken.

In the other side you're talking about don't care what is actually
related with the flow control. It turns out, after a timeout, you are no
longer interested in the result so you don't use it because you don't care.

I insist, two concepts: one for cancelling a control flow, another for an
specific operation of the implementation.
El 02/03/2015 08:06, Dean Tribble trib...@e-dean.com escribió:

 Another thread here brought up the challenge of supporting cancellation in
 an async environment. I spent some time on that particular challenge a few
 years ago, and it turned out to be bigger and more interesting than it
 appeared on the surface. In the another thread, Ron Buckton pointed at the
 .Net approach and it's use in JavaScript:


 AsyncJS (http://github.com/rbuckton/asyncjs) uses a separate abstraction
 for cancellation based on the .NET
 CancellationTokenSource/CancellationToken types. You can find more
 information about this abstraction in the MSDN documentation here:
 https://msdn.microsoft.com/en-us/library/dd997364(v=vs.110).aspx


 It's great that asyncjs already has started using it. I was surprised at
 how well the cancellationToken approach worked in both small applications
 and when extended to a very large async system. I'll summarize some of the
 architectural observations, especially from extending it to async:

 *Cancel requests, not results*
 Promises are like object references for async; any particular promise
 might be returned or passed to more than one client. Usually, programmers
 would be surprised if a returned or passed in reference just got ripped out
 from under them *by another client*. this is especially obvious when
 considering a library that gets a promise passed into it. Using cancel on
 the promise is like having delete on object references; it's dangerous to
 use, and unreliable to have used by others.

 *Cancellation is heterogeneous*
 It can be misleading to think about canceling a single activity. In most
 systems, when cancellation happens, many unrelated tasks may need to be
 cancelled for the same reason. For example, if a user hits a stop button on
 a large incremental query after they see the first few results, what should
 happen?

- the async fetch of more query results should be terminated and the
connection closed
- background computation to process the remote results into renderable
form should be stopped
- rendering of not-yet rendered content should be stopped. this might
include retrieval of secondary content for the items no longer of interest
(e.g., album covers for the songs found by a complicated content search)
- the animation of loading more should be stopped, and should be
replaced with user cancelled
- etc.

 Some of these are different levels of abstraction, and for any non-trivial
 application, there isn't a single piece of code that can know to terminate
 all these activities. This kind of system also requires that cancellation
 support is consistent across many very different types of components. But
 if each activity takes a cancellationToken, in the above example, they just
 get passed the one that would be cancelled if the user hits stop and the
 right thing happens.

 *Cancellation should be smart*
 Libraries can and should be smart about how they cancel. In the case of an
 async query, once the result of a query from the server has come back, it
 may make sense to finish parsing and caching it rather than just
 reflexively discarding it. In the case of a brokerage system, for example,
 the round trip to the servers to get recent data is the expensive part.
 Once that's been kicked off and a result is coming back, having it
 available in a local cache in case the user asks again is efficient. If the
 application spawned another worker, it may be more efficient to let the
 worker complete (so that you can reuse it) rather than abruptly terminate
 it (requiring discarding of the running worker and cached state).

 *Cancellation is a race*
 In an async system, new activities may be getting continuously scheduled
 by asks that are themselves scheduled but not currently running. The act of
 cancelling needs to run in this environment. When cancel starts, you can
 think of it as a signal racing out to catch up with all the computations
 launched to achieve the now-cancelled objective. Some of those may choose
 to complete (see the caching example above). Some may potentially keep
 launching more work before that work itself gets signaled (yeah it's a bug
 but people write buggy code). In an async system, cancellation is not
 prompt. Thus, it's infeasible