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:
OperationCanceledis 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
nameproperty 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
OperationCancelederror. - Both
dataandmessageare optional: - If
dataisundefinedit should be ignored. - If
messageis not a string it should default to"Operation Canceled". - If
datais notundefinedit is set as thedataproperty of theOperationCancelederror. - If
messageis a string it is used as themessageproperty of theOperationCancelederror. - The promise must change its state and propagate its cancelation as if
propagateCancelhad 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
propagateCancelon the promise it is waiting for. - It must return a promise for the result of calling the
onCanceledcallback attached to the resolver. - If the promise is fulfilled, its value must be
undefined. - If no
onCanceledcallback is available, or the resolver is unable to cancel, it must still return a promise, fulfilled withundefined. - If the
onCanceledcallback 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
onCanceledcallback. - The
onCanceledcallback must be ignored if it's not a function. - The first argument to the callback must be the resolver itself.
- The expected
thisvalue 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
OperationCancelederror that resulted from canceling the promise. - If the promise was indirectly canceled, the returned promise must be rejected with a generic
OperationCancelederror, with default values fordataandmessageproperties.
- Implementations may call
propagateCanceldirectly fromcancel, or not at all, as long as the end result is equivalent.