The current cancellation seems to be the most problematic feature due to some annoying limits in the design:
Awkward API for attaching the callback that cancels the work (through typed catch handler)
Cannot compose because it is impossible to tell the difference between rejection and cancellation (e.g. Promise.all(...).cancel() cannot do the obvious thing and also call cancel() on the promises in the array)
.cancel() is asynchronous
Since all consumers and producers must be trusted/"your code", there is no reason to enforce that cancellable promises are single-consumer or create new primitives.
This returns a new cancellable promise (unlike right now which just mutates existing promise which causes a lot of headache internally) and the flag will automatically propagate to all derived promises. However, the reference to the onCancel callback will only be held by the new promise created by .cancellable().
Calling .cancel() on a cancellable promise will keep propagating to cancellable parents and calling their onCancel callbacks.
The onCancel callback will receive the cancellation reason as its only argument (the default is a CancellationError instance).
From the callback it's possible to decide the fate of the promise:
Throwing an error will reject the promise with the error as the rejection reason
Returning a value will fulfill the promise with the value as the fulfillment value
(However I guess 99.99% of the time you want throw reason, so this flexibility adds a lot of inconvenience?)
Bad (should use .timeout() for this) example of usage:
function delay(value, time) {
var timerId;
return new Promise(function(resolve, reject) {
timerId = setTimeout(function() {
resolve(value);
}, time || value);
}).cancellable(function(reason) {
clearTimeout(timerId);
throw reason;
});
}
var after500ms = delay(500).catch(CancellationError, function(e) {
console.log("Your request took too long");
});
delay(250).then(function() {
after500ms.cancel();
});
The current cancellation seems to be the most problematic feature due to some annoying limits in the design:
Promise.all(...).cancel()
cannot do the obvious thing and also callcancel()
on the promises in the array).cancel()
is asynchronousSince all consumers and producers must be trusted/"your code", there is no reason to enforce that cancellable promises are single-consumer or create new primitives.
Edit: The below design has been scratched, see https://github.com/petkaantonov/bluebird/issues/415#issuecomment-73242358
In the new cancellation you would register the callback while marking the promise cancellable:This returns a new cancellable promise (unlike right now which just mutates existing promise which causes a lot of headache internally) and the flag will automatically propagate to all derived promises. However, the reference to the
onCancel
callback will only be held by the new promise created by.cancellable()
. Calling.cancel()
on a cancellable promise will keep propagating to cancellable parents and calling theironCancel
callbacks.The
onCancel
callback will receive the cancellation reason as its only argument (the default is aCancellationError
instance).From the callback it's possible to decide the fate of the promise:
(However I guess 99.99% of the time you want
throw reason
, so this flexibility adds a lot of inconvenience?)Bad (should use .timeout() for this) example of usage: