Skip to content

Proposal for PromiseKit cancellation support #896

@dougzilla32

Description

@dougzilla32

I have a proposal for adding cancellation to PromiseKit. PromiseKit is excellent for making code super clean! But I feel like it is missing a good solution for the real-world problem of cancellation. For example, with the current solution the code becomes rather involved for cancelling a chain of promises.

I’ve been working part-time for a couple months (!!) on add-ons for PromiseKit and the PromiseKit extensions that provide very good and complete (I think!) cancellation support. I hope it can be a useful addition to the PromiseKit implementation, and very useful for developers who want a comprehesive way to cancel promises!

I read up on some relevant threads and realize there are drawbacks to having a built-in ‘cancel’ method, so I'm using a delegation approach to leave the existing PromiseKit code unchanged. The core code is a set of wrappers called CancellablePromise, CancellableGuarantee, CancellableThenable, and CancellableCatchable, which delegate to their PromiseKit counterparts. There are also cancellable varients for all the 'after', 'firstly', 'hang', 'race' and 'when' PromiseKit functions. The new methods and functions mirror the public PromiseKit API, with the addition of cancellation support.

The delegate approach has the advantage that promises can be explicitly created with or without cancellation abilities. So the idea is you can get all the advantages without any of the disadvantages.

There is a class called 'CancelContext' that tracks the cancellable task(s) and 'reject' method for each promise in the chain. This allows for easy cancellation of the entire chain including branches. As each promise is initialized its cancellable task and 'reject' method is added to the CancelContext. And as each promise resolves its cancellable task and 'reject' method are removed from the CancelContext. Because these operations can happen on various threads I made the CancelContext code fully thread safe.

I’ve gone through all the PromiseKit extensions and found parts of 9 of them that are cancellable (see below), and implemented cancellable extensions for all of them.

I have a bunch of test cases for both the core code and for the extensions, so I think it is pretty solid.

Packaging

I've gotten this working very well both integrated with CorePromise and as a separate extension. I see 3 options for packaging this:

  1. Include cancellation with CorePromise — no new extensions or new dependencies. This option works very well but would sully CorePromise with cancellation code. Still, this may be acceptable as the existing code remains unchanged. Only new cancellation classes, methods and functions are added.
  2. Add cancellation as a PromiseKit extension called PMKCancel, and add a dependency on PMKCancel to each PromiseKit extension that can support cancellation — 1 new extension, 9 new dependencies. This is great solution for keeping the cancellation code separate from CorePromise, but it would require 9 existing extensions to have a new dependency on PMKCancel.
  3. Add cancellation as a PromiseKit extension called PMKCancel and make a new cancellable extension for each PromiseKit extension that can support cancellation — 10 new extensions, no new dependencies. This would require the PMKCancel extension plus 9 new 'mirror' extensions, but would not require existing extensions to have any new dependencies.

I will submit pull requests for options 1 and 2. Option 3 looks very similar to the set of projects on my github account: https://github.com/dougzilla32

I personally like option 1 best, then 2, but not so much option 3.

Next steps

I heard back from Max Howell that this might be a good addition to PMK 7. I'll keep the two pull requests for options 1 and 2 up to date with the current PromiseKit code -- the initial pull requests will be for PromiseKit 6.3.4.

I can volunteer to keep the cancellation code up to date as PromiseKit evolves. The other option would be to require contributors to update the cancellation code if any part of the public PromiseKit API is changed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions