#Promises with |end| capability
Maybe it's better to think about cancellation as a finishing? Actually just use finishing as cancellation for promises which do not just store data, but rather makes some request.
This is not a secret what browsers do some tricky thing to determine if promise chain ended so Promise and its value might by GCed. It also does not work for stored promises like window.p = new Promise(...)
. So promises needs not only cancellation, but finishing ability too. It might be combined.
Let's for example introduce .end()
(just example of name) method on the Promise.prototype
which by default lock current promise and tells to GC what no one can anymore access value of this promise. It's just becomes no-op completely (all the methods) so GC might collects its value or stop referencing it with promise.
Potentially, this also should work with chain of the promise, so once .end()
called it also tries to end chain. Of course it should use ref-counts, etc. Few examples:
var pending = new Promise(function(resolve) {
setTimeout(resolve, 1000);
});
var a = pending.then(function() {
console.log('a')
});
var b = pending.then(function() {
console.log('b')
});
// This lock promise, all methods (then, catch, end) becomes no-op.
// Ff promises was not settled before lock (as per this case) it also
// makes executor functions no-op (fullfill/reject) and locks all chained promises
// so a and b handlers will never be called.
// But if promise was already settled it does nothing for previous chained promises,
// only makes methods no-op (then, catch, end)
pending.end();
var pending = new Promise(function(resolve) {
setTimeout(resolve, 1000);
});
var a = pending.then(function() {
console.log('a')
});
var b = pending.then(function() {
console.log('b')
});
// this locks promise |b| in same way as |pending| promise in prev. example
// and also tried to lock |pending| promise, but since it has more than last consumers, this attempt fails
// here |b| handler never will be called
b.end();
// now we lock |a| promise, this locks |a| itself and also locks |pending| since |a|
// is the last consumer of the |pending|
a.end();
var doFetch = function(...) {
var req = fetch(...);
var result = req.then(function(res) {
return res.copy().json();
});
return result;
}
var pending = doFetch(...);
// this locks |pending| promise which is same as |result| promise
// in |doFetch|, since |result| is only consumer of |req| promise,
// its try to lock will success and |req| will receive internal signal to stop
pending.end();
var doFetch = function(...) {
var req = fetch(...);
var result = req.then(function(res) {
return res.copy().json();
});
req.then(function() {
// Here |result| is still in pending state, but |req|
// is settled so |result| now stores |res.copy().json()| promise as its value
// Locking |resulse| now will make it no-op and also will try to lock
// |res.copy().json()| and since there is no other consumers for it,
// action will success and Stream responsible for |res.copy().json()| will be
// notified to end
result.end();
});
return result;
}
var pending = doFetch(...);
Of course, fetch
will should have its internal handler for lock signal to abort actual network activity. But I do not think ability to override .end()
action should be provided in promise executor (new Promise(function() { /* here */ })
), rather I think it should be provided per-class. So, in ES7 (because of .end()
) one can extend Promise and override end()
action by @@somthing
. This will keep promises in their "private nature", but also will provide overridable end()
action for subclass, such as FetchPromise.
This is rough proposal which does not cover all cases (yet), but I will work on it.