This proposal specifies how cancelation is triggered, handled and propagated in a Promises/A+ promise library.
In addition to the terminology from Promises/A+ we use the following:
OperationCanceled
is an error used to reject canceled promises.- "direct cancelation" is when a promise is canceled by the consumer of the promise calling 'cancel'.
- "indirect cancelation" is when a promise is canceled as the result of another promise that was waiting on it being directly or indirectly canceled.
When a promise is directly canceled it is rejected with an OperationCanceled
error. This error must obey the following points:
- It must be an instance of error (
error instanceof Error === true
). - It must have a
name
property with value"OperationCanceled"
.
When the cancel
method is called on a promise it is directly canceled. The cancel
method accepts two arguments:
promise.cancel(data, message);
- The promise must be rejected with an
OperationCanceled
error. - Both
data
andmessage
are optional: - If
data
isundefined
it should be ignored. - If
message
is not a string it should default to"Operation Canceled"
. - If
data
is notundefined
it is set as thedata
property of theOperationCanceled
error. - If
message
is a string it is used as themessage
property of theOperationCanceled
error. - The promise must change its state and propagate its cancelation as if
propagateCancel
had been called, and return the result of the propagation (meaning a promise is returned) [3.1].
The propagateCancel
method is intended only to be called by this and other promises, it is not for external use.
When propagateCancel
is called, the promise transitions into an extra canceled
state. This does not trigger any events. It does however mean that the promise can never be resolved (i.e. it never leaves the canceled
state).
- If the promise is waiting on another promise to complete it may call
propagateCancel
on the promise it is waiting for. - It must return a promise for the result of calling the
onCanceled
callback attached to the resolver. - If the promise is fulfilled, its value must be
undefined
. - If no
onCanceled
callback is available, or the resolver is unable to cancel, it must still return a promise, fulfilled withundefined
. - If the
onCanceled
callback throws an exception, the promise must be rejected with that exception as its reason.
In most cases it should call propagateCancel
on the promise it's waiting for. The exception is if the promise has been in some way 'forked', when it may choose not to in an implementation specific way.
An onCanceled
callback can be added to the resolver. The onCanceled
callback accepts one argument:
resolver.onCanceled = function(resolver){
};
- When a promise is canceled (directly or indirectly) the resolver for that promise must invoke its
onCanceled
callback. - The
onCanceled
callback must be ignored if it's not a function. - The first argument to the callback must be the resolver itself.
- The expected
this
value inside the callback is left unspecified. - The resolver may remove the callback once it's no longer pending.
- A resolver must ignore propagated cancelations if there are other promises depending on it.
- Conversely, a resolver can only be indirectly canceled if that cancelation comes from the only promise that depends on it.
- If the promise was directly canceled, the returned promise must be rejected with the
OperationCanceled
error that resulted from canceling the promise. - If the promise was indirectly canceled, the returned promise must be rejected with a generic
OperationCanceled
error, with default values fordata
andmessage
properties.
- Implementations may call
propagateCancel
directly fromcancel
, or not at all, as long as the end result is equivalent.