Skip to content

Releases: lilyball/Tomorrowland

v1.4.0

28 Jul 05:05
Compare
Choose a tag to compare
  • Fix the cancellation propagation behavior of Promise.Resolver.resolve(with:) and the flatMap family of methods. Previously, requesting cancellation of the promise associated with the resolver (for resolve(with:), or the returned promise for the flatMap family) would immediately request cancellation of the upstream promise even if the upstream promise had other children. The new behavior fixes this such that it participates in automatic cancellation propagation just like any other child promise (#54).

  • Slightly optimize stack usage when chaining one promise to another.

  • Avoid using stack space for chained promises that don't involve a callback. For example, when the promise returned from a flatMap(on:token:_:) resolves it will resolve the outer promise without using additional stack frames. You can think of it like tail calling functions. This affects not just flatMap but also operations such as tap(), ignoringCancel(), and more. This also applies to Obj-C (with TWLPromise).

    Note: This does not affect the variants that implicitly upcast from some E: Swift.Error to Swift.Error such as tryFlatMap(on:token:_:).

  • Change cancellation propagation behavior of onCancel. Like tap, it doesn't prevent automatic cancellation propagation if the parent has other children and all other children request cancellation. Unlike tap, requesting cancellation of onCancel when there are no other children will propagate cancellation to the parent. The motivation here is attaching an onCancel observer shouldn't prevent cancellation that would otherwise occur, but when it's the only child it should behave like the other standard observers (#57).

  • Add method Promise.makeChild(). This returns a new child of the receiver that adopts the receiver's value and propagates cancellation like any other observer. The purpose here is to be used when handing back multiple children of one parent to callers, as handing back the parent means any one caller can cancel it without the other callers' participation. This is particularly useful in conjunction with propagatingCancellation(on:cancelRequested:) (#56).

v1.3.0

24 May 02:22
Compare
Choose a tag to compare
  • Add PromiseContext.isExecutingNow (TWLPromiseContext.isExecutingNow in Obj-C) that returns true if accessed from within a callback registered with .nowOr(_:) and executing synchronously, or false otherwise. If accessed from within a callback (or Promise.init(on:_:)) registered with .immediate and running synchronously, it inherits the surrounding scope's PromiseContext.isExecutingNow flag. This is intended to allow Promise(on: .immediate, { … }) to query the surrounding scope's flag (#53).
  • Add convenience methods to Obj-C for doing then+catch together, as this is a common pattern and chaining Obj-C methods is a little awkward (#45).
  • Change Promise.timeout's default context to .nowOr(.auto) for the Error overload as well.
  • Change the behavior of Promise.timeout(on:delay:) when the delay is less than or equal to zero, the context is .immediate or .nowOr(_:), and the upstream promise hasn't resolved yet. Previously the timeout would occur asynchronously and the upstream promise would get a chance to race the timeout. With the new behavior the timeout occurs synchronously (#49).

v1.2.0

22 May 06:52
Compare
Choose a tag to compare
  • Add PromiseContext.nowOr(context) (+[TWLContext nowOrContext:] in Obj-C) that runs the callback synchronously when registered if the promise has already resolved, otherwise registers the callback to run on context. This can be used to replace code that previously would have required checking promise.result prior to registering the callback (#34).

    For example:

    networkImagePromise.then(on: .nowOr(.main), { [weak button] (image) in
        button?.setImage(image, for: .normal)
    })
  • Add Promise.Resolver.hasRequestedCancel (TWLResolver.cancelRequested in Obj-C) that returns true if the promise has been requested to cancel or is already cancelled, or false if it hasn't been requested to cancel or is fulfilled or rejected. This can be used when a promise initializer takes significant time in a manner not easily interrupted by an onRequestCancel handler (#47).

  • Change Promise.timeout's default context from .auto to .nowOr(.auto). This behaves the same as .auto in most cases, except if the receiver has already been resolved this will cause the returned promise to likewise already be resolved (#50).

  • Ensure when(first:cancelRemaining:) returns an already-cancelled promise if all input promises were previously cancelled, instead of cancelling the returned promise asynchronously (#51).

  • Ensure when(fulfilled:qos:cancelOnFailure:) returns an already-resolved promise if either all input promises were previously fulfilled or any input promise was previously rejected or cancelled (#52).

v1.1.1

01 Apr 23:20
Compare
Choose a tag to compare
  • Fix memory leaks in PromiseInvalidationToken.requestCancelOnInvalidate(_:) and PromiseInvalidationToken.chainInvalidation(from:includingCancelWithoutInvalidating:) when cleaning up nil nodes prior to pushing on the new node (#48).

v1.1.0

26 Nov 19:10
Compare
Choose a tag to compare
  • Add new method .propagatingCancellation(on:cancelRequested:) that can be used to create a long-lived promise that propagates cancellation from its children to its parent while it's still alive. Normally promises don't propagate cancellation until they themselves are released, in case more children are going to be added. This new method is intended to be used when deduplicating requests for an asynchronous resource (such as a network load) such that the resource request can be cancelled in the event that no children care about it anymore (#46).

v1.0.1

26 Nov 19:10
Compare
Choose a tag to compare
  • Suppress a warning from the Swift 5.1 compiler about code that the Swift 5.0 compiler requires.

v1.0.0

19 May 22:25
Compare
Choose a tag to compare
  • Fix a rather serious bug where PromiseInvalidationTokens would not deinit as long as any promise whose callback was tied to the token was still unresolved. This meant that the default invalidateOnDeinit behavior would not trigger and the callback would still fire even though there were no more external references to the token, and this meant any promises configured to be cancelled when the promise invalidated would not cancel. Tokens used purely for requestCancelOnInvalidate(_:) would still deallocate, and tokens would still deallocate after any associated promises had resolved.
  • Tweak the atomic memory ordering used in PromiseInvalidationTokens. After a careful re-reading I don't believe I was issuing the correct fences previously, making it possible for tokens whose associated promise callbacks were executing concurrently with a call to requestCancelOnInvalidate(_:) to read the wrong generation value, and for tokens that had requestCancelOnInvalidate(_:) invoked concurrently on multiple threads to corrupt the generation.
  • Add PromiseInvalidationToken.chainInvalidation(from:) to invalidate a token whenever another token invalidates. This allows for building a tree of tokens in order to have both fine-grained and bulk invalidation at the same time. Tokens chained together this way stay chained forever (#43).
  • Update project file to Swift 5.0. The source already supported this. This change should only affect people using [Carthage][] or anyone adding building this framework from source.
  • Update the podspec to list both Swift 4.2 and Swift 5.0. With CocoaPods 1.7.0 or later your Podfile can now declare which version of Swift it's compatible with. For anyone using CocoaPods 1.6 or earlier it will default to Swift 5.0.

v0.6.0

27 Apr 06:53
Compare
Choose a tag to compare
  • Make DelayedPromise conform to Equatable (#37).
  • Add convenience functions for working with Swift.Result (#39).
  • Mark all the deprecated functions as unavailable instead. This restores the ability to write code like promise.then({ foo?($0) }) without it incorrectly resolving to the deprecated form of map(_:) (#35).
  • Rename Promise.init(result:) and Promise.init(on:result:after:) to Promise.init(with:) and Promise.init(on:with:after:) (#40).

v0.5.1

07 Apr 06:44
Compare
Choose a tag to compare
  • When chaining multiple .main context blocks in the same runloop pass, ensure we release each block before executing the next one.

  • Ensure that if a user-supplied callback is invoked, it is also released on the context where it was invoked (#38).

    This guarantee is only made for callbacks that are invoked (ignoring tokens). What this means is when using e.g. .then(on:_:) if the promise is fulfilled, the onSuccess block will be released on the provided context, but if the promise is rejected no such guarantee is made. If you rely on the context it's released on (e.g. it captures an object that must deallocate on the main thread) then you can use .always or one of the mapResult variants.

v0.5.0

26 Feb 06:46
Compare
Choose a tag to compare
  • Rename a lot of methods on Promise and TokenPromise (#5).

    This gets rid of most overrides, leaving the only overridden methods to be ones that handle either Swift.Error or E: Swift.Error, and even these overrides are removed in the Swift 5 compiler.

    then is now map or flatMap, recover's override is now flatMapError, always's override is now flatMapResult, and similar renames were made for the try variants.

  • Add a new then method whose block returns Void. The returned promise resolves to the same result as the original promise.

  • Add new mapError and tryMapError methods.

  • Add new mapResult and tryMapResult methods.

  • Extend tryFlatMapError to be available on all Promises instead of just those whose error type is Swift.Error.

  • Remove the default .auto value for the on context: parameter to most calls. It's now only provided for the "terminal" callbacks, the ones that don't return a value from the handler. This avoids the common problem of running trivial maps on the main thread unnecessarily (#33).


Note: When upgrading to this version from previous versions, expect a lot of deprecation warnings. Not all of the warnings are accurate. In particular, code that looks like

somePromise().then({ [weak self] value in
    self?.doSomething(with: value)
})

will raise a warning suggesting this should be map(on:_:), due to the inferred ()? return type. This can be suppressed by writing value -> Void as the type signature instead. A future release will remove this deprecation warning.