Releases: lilyball/Tomorrowland
v1.4.0
-
Fix the cancellation propagation behavior of
Promise.Resolver.resolve(with:)
and theflatMap
family of methods. Previously, requesting cancellation of the promise associated with the resolver (forresolve(with:)
, or the returned promise for theflatMap
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 justflatMap
but also operations such astap()
,ignoringCancel()
, and more. This also applies to Obj-C (withTWLPromise
).Note: This does not affect the variants that implicitly upcast from some
E: Swift.Error
toSwift.Error
such astryFlatMap(on:token:_:)
. -
Change cancellation propagation behavior of
onCancel
. Liketap
, it doesn't prevent automatic cancellation propagation if the parent has other children and all other children request cancellation. Unliketap
, requesting cancellation ofonCancel
when there are no other children will propagate cancellation to the parent. The motivation here is attaching anonCancel
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 withpropagatingCancellation(on:cancelRequested:)
(#56).
v1.3.0
- Add
PromiseContext.isExecutingNow
(TWLPromiseContext.isExecutingNow
in Obj-C) that returnstrue
if accessed from within a callback registered with.nowOr(_:)
and executing synchronously, orfalse
otherwise. If accessed from within a callback (orPromise.init(on:_:)
) registered with.immediate
and running synchronously, it inherits the surrounding scope'sPromiseContext.isExecutingNow
flag. This is intended to allowPromise(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 theError
overload as well. - Change the behavior of
Promise.timeout(on:delay:)
when thedelay
is less than or equal to zero, thecontext
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
-
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 oncontext
. This can be used to replace code that previously would have required checkingpromise.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 returnstrue
if the promise has been requested to cancel or is already cancelled, orfalse
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 anonRequestCancel
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
v1.1.0
- 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
v1.0.0
- Fix a rather serious bug where
PromiseInvalidationToken
s would not deinit as long as any promise whose callback was tied to the token was still unresolved. This meant that the defaultinvalidateOnDeinit
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 forrequestCancelOnInvalidate(_:)
would still deallocate, and tokens would still deallocate after any associated promises had resolved. - Tweak the atomic memory ordering used in
PromiseInvalidationToken
s. 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 torequestCancelOnInvalidate(_:)
to read the wrong generation value, and for tokens that hadrequestCancelOnInvalidate(_:)
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
- Make
DelayedPromise
conform toEquatable
(#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 ofmap(_:)
(#35). - Rename
Promise.init(result:)
andPromise.init(on:result:after:)
toPromise.init(with:)
andPromise.init(on:with:after:)
(#40).
v0.5.1
-
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, theonSuccess
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 themapResult
variants.
v0.5.0
-
Rename a lot of methods on
Promise
andTokenPromise
(#5).This gets rid of most overrides, leaving the only overridden methods to be ones that handle either
Swift.Error
orE: Swift.Error
, and even these overrides are removed in the Swift 5 compiler.then
is nowmap
orflatMap
,recover
's override is nowflatMapError
,always
's override is nowflatMapResult
, and similar renames were made for thetry
variants. -
Add a new
then
method whose block returnsVoid
. The returned promise resolves to the same result as the original promise. -
Add new
mapError
andtryMapError
methods. -
Add new
mapResult
andtryMapResult
methods. -
Extend
tryFlatMapError
to be available on allPromise
s instead of just those whose error type isSwift.Error
. -
Remove the default
.auto
value for theon 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.