Last active
February 2, 2017 03:41
-
-
Save mgtitimoli/b954819f8a734f46876f5c59dab972f4 to your computer and use it in GitHub Desktop.
Cancellable Promises (an implementation without cancellation tokens)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// API: | |
// | |
// Promise.prototype.cancel(...args): | |
// | Promise<resolved> // if promise was cancellable and no error happened | |
// | Promise<rejected> // if promise was not cancellable or some error happened | |
// | |
// type CancellationHandler = (...args) => | |
// | Promise<rejected> // can be returned manually or automatically generated by throwing an error | |
// | Promise<resolved> // can be returned manually or returning without throwing an error | |
// | |
// type CanCancel = (onCancel: ?CancellationHandler) => Promise; | |
// | |
// type Executor = ( | |
// resolve: ?function, | |
// reject: ?function, | |
// canCancel: ?CanCancel, // calling this function will enable promise to be cancellable | |
// ) => any; | |
// | |
// [constructor] Promise(executor: Executor): Promise | |
const produceCancellablePromise = allowedConsumerId => | |
new Promise((resolve, reject, canCancel) => { | |
// calling promise.cancel will return a new | |
// promise that tracks cancellation termination | |
// so this handler acts like a promise executor | |
// 1. if an exception is thrown | |
// - cancellation is prevented | |
// - error can be caught attaching a handler to catch method | |
// of the promise returned by promise.cancel | |
// 2. if no exception is thrown | |
// - cancellation is allowed | |
// - attaching a handler to then method of the promise returned by promise.cancel | |
// will allow to know when cancellation finished and get the corresponding message (if any) | |
const onCancel = ( | |
// all the args passed to promise.cancel will be received as params here | |
consumer | |
) => { | |
if (consumer.id !== allowedConsumerId) { | |
throw new Error('consumer not allowed'); | |
} | |
return 'consumer allowed'; | |
}; | |
// Calling this function, will allow the execution of a new method: | |
// promise.cancel | |
// 1. If canCancel is not executed, then any call to promise.cancel | |
// will return a rejected promise | |
// 2. Regarding to the cancellation handler | |
// 2.1. If it is provided then it will be able to: | |
// 2.1.1. prevent cancellation -> throw an error or return a reject promise | |
// 2.1.2. allow cancellation -> run without throwing an error or return a resolved promise | |
// 2.2 if no cancellation handler is provided, then there will be | |
// no restriction to cancel, and any consumer will be able to do it | |
canCancel(/* optional */ onCancel); | |
setTimeout(resolve, 5000); | |
}); | |
const consumePromise = (consumer, promise) => | |
promise | |
.cancel(consumer) | |
.then(console.log); | |
const consumers = { | |
{id: Symbol('consumer 1')}, | |
{id: Symbol('consumer 2')}, | |
}; | |
const cancellableByConsumer0 = produceCancellablePromise(consumers[0]); | |
// consumers match => this consumer is able to cancel (see line #23) | |
consumePromise(cancellableByConsumer0, consumers[0]); | |
// consumers do not match => this consumer is not able to cancel (see line #23) | |
consumePromise(cancellableByConsumer0, consumers[1]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment