Git Product home page Git Product logo

deferred's Introduction

Deferred

Warning

Deferred is deprecated in favor of Swift Concurrency. For migration hints, see Migrating to Swift Concurrency.

Deferred let you work with values that haven't been determined yet, like an array that's coming later (one day!) from a web service call. It was originally inspired by OCaml's Deferred library.

Deferred is a "futures library", probably like ones you've already heard about. Where Deferred aims to be different is by providing a small, efficient API that's easily adopted in our many consulting projects.

Vital Statistics
Requires Swift 5.1 or greater
Under MIT License
Multiplatform
Circle CI
CocoaPods
Swift Package Manager

Don't Panic

See Deferred's comprehensive programming guide and documentation at the Deferred Reference.

If you have a question not answered by the guide or the module comments, please open an issue!

Need Some Help?

Deferred, as an open source project, is free to use and always will be. Big Nerd Ranch offers help for Deferred, Code Audits, and general mobile app design/development services. Email us at [email protected] to get in touch with us for more details.

deferred's People

Contributors

bolot avatar cbkeur avatar davidahouse avatar hitsvilleusa avatar hydhknn avatar jaredsinclair avatar jeremy-w avatar kevinrandrup avatar mps avatar natechan avatar piercifani avatar zwaldowski avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

deferred's Issues

Task might conflict with Foundation

A problem of the may-actually-be-worse-in-my-head-than-in-reality variety, but Deferred.Task shadows Foundation.Task. Any generics misuses or error involving Task tends to instead error about misuse of the Foundation type instead.

3.0.0beta1 is not available on Cocoapods

Installation fails with:

[!] Unable to satisfy the following requirements:

- `BNRDeferred (= 3.0.0-beta.1)` required by `BSWFoundation (0.2.0)`

None of your spec sources contain a spec satisfying the dependency: `BNRDeferred (= 3.0.0-beta.1)`.

Apply Swift 3 API Guidelines

Bikeshed time. See also #20, #21, and #66.

Are you a BNR person assigned to this issue? You've been assigned for review! You don't need to do anything other than read through this issue and leave feedback.

This document refers to the Swift 3-style name of types unless explicitly referring to the old API.

Proposed changes

- `Deferred.init(value:)` ➡️ `Deferred.init(filledWith:)` - `ExecutorType` ➡️ `Executor` - `ExecutorType.submit(_:)` ➡️ `Executor.async(_:)` - `Sequence.earliestFilled` ➡️ `Sequence.firstCompleted()` - `Collection.joinedValues` ➡️ `Collection.allCompleted()` - `FutureType` ➡️ `FutureProtocol` - `FutureType.wait(_:)` ➡️ `FutureProtocol.wait(until:)` - `FutureType.Type.genericQueue` ➡️ `DispatchQueue.Type.any()` (i.e., `upon(.any()) { ... }`) - `FutureType.uponMainQueue(_:)` ➡️ removed (use `upon(.main)`) - `FutureType.upon(:_:)` ➡️ `FutureType.upon(_:execute:)` - `FutureType.map(upon:_:)` ➡️ `FutureType.map(upon:transform:)` - `FutureType.flatMap(upon:_:)` ➡️ `FutureType.andThen(upon:start:)` - `IgnoringFuture` ➡️ remove (#75) - `LockProtected` ➡️ `Protected` - `PromiseType` ➡️ `PromiseProtocol` - `PromiseType.fill(_:)` ➡️ `PromiseProtocol.fill(with:)` - `PromiseType.fill(_:assertIfFilled:file:line:)` ➡️ `PromiseProtocol.mustFill(with:)` - `ReadWriteLock` ➡️ `Locking` - `Timeout` ➡️ removed, use Dispatch timeouts #### Task - introducing ➡️ `Task.init(executing:upon:)` - `Task.init(upon:per:onCancel:body:)` ➡️ `Task.init(executedUpon:per:cancellingWith:body:)` - `IgnoringTask` ➡️ removed (see also #75) - `PromiseType.succeed(_:)` ➡️ `PromiseProtocol.succeed(with:)` - `PromiseType.fail(_:)` ➡️ `PromiseProtocol.fail(with:)` - `PromiseType.fill(with:)` ➡️ removed - `TaskAccumulator` ➡️ `TaskGroup` - `TaskAccumulator.accumulate(_:)` ➡️ `TaskGroup.include(_:)` - `TaskAccumulator.allCompleted()` ➡️ `TaskGroup.completed()` - `Collection.joinedTasks` ➡️ `Collection.allSucceeded()` - `FutureType.recover(upon:_:)` ➡️ `FutureProtocol.recover(upon:substituting:)` - `TaskResult.Success` ➡️ `TaskResult.success` - `TaskResult.Failure` ➡️ `TaskResult.failure` - `ResultType` ➡️ `Either` - introducing ➡️ `Either.Left` (`associatedtype Left = Error`) - `ResultType.Value` ➡️ `Either.Right` - `ResultType.withValues(ifSuccess:ifFailure:)` ➡️ `Either.withValues(left:right:)` - `ResultType.init(value:)` ➡️ removed - `ResultType.extract()` ➡️ `ResultProtocol.extracted()`
### Places for improvement/undecided
- [x] Collection tactics - `Sequence.firstCompleted()` - `Collection.allCompleted()` - `TaskGroup. completed()` - `Collection.allSucceeded()` - [x] Function body parameter external names

We currently lean towards unnamed function body parameters. In practice, both discussing these methods and using them is confusing.

  • Swift 3's Dispatch uses execute.
  • Should we have unique body names for all variants (upon, map, flatMap, recover), or just one?
  • Swift 3 also does away with body parameter names in a couple of places, notably reduce.

Read more.

These don't feel similar, even though they are.

  • FutureProtocol.Value, PromiseProtocol.Value, Protected<Value>

The names of these associated types or generic placeholders are wimpy. In the Task code in particular, they lead to nesting like where Future.Value.Value: Foo.

  • TaskResult And friends

See also and more.

  • Defaulted upon parameters

The current pattern of upon arguments falling back to a default queue has led to some confusion, and contributes to code duplication (see above). Do we just do away with it?

@jgallagher votes to axe it. @zwaldowski is mixed.

We might be able to split the difference and relegate the current behavior to an extension DispatchQueue.Type.any, a la future.upon(.any) { … }

Removing this would ameliorate the code duplication concerns above.

  • FutureProtocol.peek(), FutureProtocol.value

I personally don't like them. They're already test-only.

  • FutureProtocol.wait(until:)

FutureProtocol.wait(until:), like sane people, or FutureProtocol.wait(deadline:), like DispatchQueue.asyncAfter(deadline:execute:)?

  • FutureProtocol.andThen(upon:start:) (#21)
  • Old name is flatMap(upon:_:) to match stdlib.
  • Rust uses andThen.
  • Scala uses bind (as did Deferred 1.0).
  • flatMap is confusing for many users.
  • preconditionFilled(with:)

See discussion in #105.

Does "any" make sense?

From what I can suss out in OCaml-ese, our any(_:) is correct. But is it useful? When are you going to unpack the outer Deferred and not need the innermost one immediately?

Include Result in Deferred

Right now, binding different Deferred<Result<T>> is very cumbersome and requires calls to bind and resultToDeferred.

For example, parsing a struct Product from a JSON might involve code like this:

    public func fetchData(url : NSURL) -> Deferred<Result<NSData>>
    public func productFromData(data : NSData) -> Deferred<Result<Product>>

    public func sampleRequest () -> Deferred<Result<Product>> {
        let someURL = NSURL()
        return fetchData(someURL).bind({resultToDeferred($0, productFromData)})
    }

If instead we would make Deferred hold a Result instead of a generic T, we could write a custom operator allowing us to make code like above a lot more readable, like this:

    public func sampleRequest () -> Deferred<Result<Product>> {
        let someURL = NSURL()
        return fetchData(someURL) |=> productFromData
    }

Is this something you will consider as an improvement?

provide a context-specific alias for flatMap to assist in learning the API

flatMap is a generic term that's great for people who can come at this from that level already, but does a disservice to people whose first exposure to something like this is Deferred. Providing a more deferred-specific API with a more suggestive name alongside flatMap would hopefully allow them to ramp up to understanding flatMap in this context.

Suggestions:

  • andEventually
  • eventually
  • whenResolved
  • andThen

The tricky part to me is that these all could just as well be read as aliases for map, since the difference between map and flatMap comes down to map returning a Deferred<Value> that's been resolved from the get-go, while flatMap allows you to return any flavor of Deferred<Value>.

Linux support

This is a tracking ticket for improvements related to multi-platform support.

  • #if os-out use of NSProgress
  • Set up CI

Task

This is a tracking issue for what I imagine to be several PRs. Work is ongoing at branch zwaldowski/task and bignerdranch/Task. Vomiting my plan out on the page because I'm taking vacation. 😎 🚢

Deferred is very often used to express the result of operations that can naturally fail, expressed rationally as Deferred<Result<T>>. Deferred should include extensions to make this easier.

  • Add an extremely barebones Result and ResultType.
    • Full test coverage.
  • Subtree merge a cleaned up version of the work at bignerdranch/Task (private repo).
  • Tree-filter to move Task/**/*.swift to Sources/Task.
  • Tree-filter to move Task/TaskTests/**/*.swift to Tests.
  • Implement (or finish implementing) Result primitives as FutureType where Value: ResultType extensions.
    • uponSuccess and uponFailure
    • map and flatMap overloads (or mapSuccess and flatMapSuccess)
      • Should flatMap versions throw instead of returning Result?
    • recover and recoverWith (or mapFailure and flatMapFailure)
    • async convenience initializers (init(_: () -> T), init(_: () throws -> T) for Result versions)
    • flatten
  • Eating our vegetables.
    • Documentation coverage for new code.
    • As-high-as-possible test coverage.

[3.0.0beta1] Crash on observing NSProgress

Hello,

Sorry to bug again, but I'm finding another crash when (attempting) to observe the progress of a task. I created a gist that again reproduces the crash.

Thanks for your time and I can't thank you enough for your work on this library!

[README] Table-of-contents-ify

As mentioned in #78:

@jeremy-w: This README might have reached the point where a Table of Contents is appropriate. I generally use doctoc for that (npm -g install tocdoc).

And:

@zwaldowski: @jeremy-w To that point, the TOC'ed and usage-based readme should probably be transcluded in Jazzy, as if it were the "Deferred Type Programming Guide" (as long as we're going for Apple-like).

And:

@zwaldowski: Totally outside the scope of this PR, but just for reference I had in mind what ReSwift does — it's sort of the poster child for Jazzy. Notice that they split up their README into different Markdown documents that get used in the documentation set.

Remove Box<T>

With SE-0116, we can perhaps use as AnyObject to treat everything as object-pointer-sized.

CocoaPods Support

Hello,

In the Readme it's specified that the project doesn't support CocoaPods but the repository contains a .podspec file. When trying to install it with pod 'Deferred', another pod with the same name is installed. Do you have any idea about what's going on?

Thanks a lot!

Setup continuous integration

  • Swift 2.0 Travis CI
    • swift-2_0 branch protection
    • swift-2_0 branch require status checks
  • Swift 1.2 Travis CI
    • master branch protection
    • master branch require status checks
  • Turn off per-push builds for Travis (PR builds only)

Ignoring semantics

The original Deferred describes unit and all_unit, which produce Deferred<Void>. This has been useful in our own apps and for derived types.

A simple implementation could be no different from:

deferred.map { _ in }

However, there might be some optimization that could be made in the GCD implementation (#13) by reusing the queue and block or refusing to create storage. Some effort would ideally be made to prevent that derived Deferred from being filled at all.

[3.0.0beta1] Crash on allSucceded

Hey guys!

I'm experiencing a crash on allSucceded where it's crashing on calling adoptChild(_:orphaned:pendingCount:) on NSProgress. The exception states that the reason is: NSProgress 0x60000032fbe0 was already the child of another progress 0x60800032abe0'

I can reproduce it 100% of the time on my code but I couldn't make it crash using a simpler project.

Deferred doesn't participate in QoS Overrides

From WWDC 2015 Session 212:

screen shot 2015-07-13 at 1 43 42 pm

Deferred currently uses dispatch_group_wait and dispatch_async for chaining, which can exclude participation in QoS propagation and contribute to priority inversion issues. Use of the dispatch_block APIs should be investigated for Deferred chaining.

Fix inappropriate use of computed properties in Future collections

The Future Collection extensions do not follow the Swift conventions for computed properties. In the spirit of the stdlib, getters that aren't idempotent nor O(1) should be documented as such and ideally be methods. Examples in the stdlib include generate(), popFirst(), and underestimatedCount(). Since earliestFilled and joinedValues are both O(n) in terms of the receiving collection and have threading implications, they very much fall under the "performs a calculation" rule of thumb for being methods.

rename `fillIfUnfulfilled` to `fill`

This is a "gotcha" in the API for now. It's common to have multiple producers for a deferred value (say, both a network operation AND possibly the user smacking a cancel button); having to remember to do fillIfUnfulfilled or risk exploding is a wart from my point of view.

peek(), isFilled return nil/false after fill()

The following test demonstrates the issue (should be added to ExistentialFutureTests):

func testFill() {
    let deferred = Deferred<Int>()
    anyFuture = Future(deferred)
    XCTAssertFalse(anyFuture.isFilled)               // pass
    deferred.fill(42)
    XCTAssertNotNil(anyFuture.peek())                // fail
    XCTAssertTrue(anyFuture.isFilled)                // fail
    XCTAssertNotNil(anyFuture.wait(.Now))            // fail
    XCTAssertNotNil(anyFuture.wait(.Interval(0.1)))  // pass
}

The problem can also be observed on a pure Deferred (not wrapped in Future).

In Deferred.wait, it appears that dispatch_block_wait() returns timeout/error when called with a timeout of zero/now, as the "upon" block (which assigns value = $0) is only queued and has't completed.

Should `Deferred.value` be made internal?

Many of us have said the blocking value getter "only" exists for the unit tests. With the advent of @testable in Swift 2, does this mean we can/could/should mark the method as internal?

Dis-integrate "docs.md"

The legacy docs.md contains some high-level documentation and usage examples. The high-level stuff should be pulled into the README (if there is any mismatch; the README is already comprehensive). The usage examples should be moved into the HeaderDoc.

Fixtures.swift included in podspec

It looks like 3.0.0-beta.1 added Fixtures.swift to the files to be compiled with the module, which makes compilation brake when building for iOS using Cocoapods. Screenshot here

This is due to the fact that this file (that imports XCTest, which is not allowed on non-test targets) is now on Sources/TestSupport, so the podspec picks this file up for compiling the module, since it's in the Sources directory.

Let me know if you need any more info on this

Introduce "lazyMap" API

This issue seeks to resurrect #75 with a limited scope, deciding on a canonical name for lazyMap and using it to replace IgnoringFuture and IgnoringTask.

rename `Deferred` to `Eventually`?

I think the name would be more suggestive of the semantics if Deferred were named Eventually.

If we separate the future value from the ability to fill it, we'd likely have an internal Deferred with public "faces" Eventually<Value> and Resolve<Value>. I'm not sold on the Resolve bit – Determine, Fix, Produce, Supply. Resolve has the advantage that you can resolve.to(v), though.

[README] Explain Future and Promise Types

Deferred preferred interface is through Future and Promise Types. We should update the README to point people toward this usage and appropriate documentation.

Short version: Future and Promise split the 2 sides of the Deferred API. Future is read-only, Promise is write-only. So you make a Deferred, ensure the Promise view has only one owner, and give the Future out to anyone that wants it.

Require `LockProtected` members to be `rethrows`.

Instead of withReadLock<Return>(@noescape body: Value -> Return) -> Return, we should have withReadLock<Return>(@noescape body: Value -> throws Return) rethrows -> Return so you can throw across lock boundaries. Same with withWriteLock.

[3.0.0beta1] Crash on TaskProgress

I cannot recreate it on a smaller project. But I'm getting consistent crashes on latest commit of the Swift 3 branch. The backtrace looks like this:

The logger says:

_TtC8DeferredP33_9528CFABF28443747CE301F7DAD432BB13ProxyProgress object 0x127c17fa0 overreleased while already deallocating; break on objc_overrelease_during_dealloc_error to debug

Building for ARM64 in DEBUG. Please let me know if there is a way to help debug this, and as always: THANKS!

Is LockProtected still needed?

In the wake of #30, should LockProtected still be a part of the project?

In #13, @jeremy-w argues:

Why not remove LockProtected.swift and its tests file?

I argued:

I did not feel the need to remove API that is currently in use; LockProtected has been used in every project that's used Deferred so far.

And his follow-up:

It looks like dead code from the perspective of all the code remaining in Deferred. If it's effectively getting equal footing, we should maybe add some docs on it to the README as well?

Now, I'm not as sure.

Decouple from `dispatch_queue_t`

Splitting off from #54:

  • Make use of dispatch_queue_t generic or pluggable in some way. (Executor concept)

This aims to solve two problems: "What if I don't have a dispatch queue?" and "What if the platform doesn't have dispatch queues?"

For the former, it would be advantageous to have upon arguments take something queue-like, rather than a literal dispatch_queue_t. In Cocoa, these may be NSOperationQueue, NSManagedObjectContext, etc.

For the later, after briefly discussing with @jgallagher, we want to keep the conceptual model simple; rather than guarding upon availability to not-Linux, or to have more complex routines (like spinning up pthreads), we're going to wait for general Dispatch availability, since that'll be in the Swift 3.0/Deferred 3.0 time range anyway. This means that #54 is officially on ice for now.

API effects

API changes in this realm should be backwards-compatible.

Increased-arity versions of `and`.

Common criticism involves the limited uses of and. We should provide 3- and 4-arity versions as well, and recommend ways to handle greater compositions.

Possible gotcha with 2.0.0's Future.init()

In 2.0b4 I believe the following code resulted in a filled future:

let filledVoidFuture = Future<Void>()

...because the value parameter was unnamed (init(_ value: Value), link). In 2.0.0, however, init() was added, and so the same code now creates a "future that will never get fulfilled".

I'm not sure this is an "issue" that needs to be resolved in any way, but I thought it worth mentioning here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.