GithubHelp home page GithubHelp logo

promise's People

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

promise's Issues

`then` methods type inferring issue

There are 3 methods titled then on a Promise object

public func then(on queue: ExecutionContext = DispatchQueue.main, _ onFulfilled: @escaping (Value) -> (), _ onRejected: @escaping (Error) -> () = { _ in }) -> Promise<Value>

public func then<NewValue>(on queue: ExecutionContext = DispatchQueue.main, _ onFulfilled: @escaping (Value) throws -> NewValue) -> Promise<NewValue>

public func then<NewValue>(on queue: ExecutionContext = DispatchQueue.main, _ onFulfilled: @escaping (Value) throws -> Promise<NewValue>) -> Promise<NewValue>

onFulfilled of the first method returns Void, of the second - NewValue, and the third - Promise<NewValue>, that's important.

If you were to write

Promise { fulfill, reject in
    fulfill(3)
}.then { result in
     return
}

then it's dandy, the Void is returned
If you were to write

Promise { fulfill, reject in
    fulfill(3)
}.then { result in
     return Promise(value: result)
}

then it's also dandy, the swiftc will figure out that the return type is Promise
However

Promise { fulfill, reject in
    fulfill(3)
}.then { result in
     return result
}

never just works on its own; there is an issue with type inference and compiler errors with "expected type Promise<_>"
The way you do it so it gets figured out is

Promise { fulfill, reject in
    fulfill(3)
}.then { result -> Int in
     return result
}

Then it's alright.

Observed in XCode 9.2 GM, Swift 4.0.2
Also observed in Swift 4.0.3 environment on Linux.

Possible solution

It may or may not help to rename the handler from onFulfilled to a different name such as onFulfilledWithValue in the method where handler returns NewValue.

Difficulty understanding tapping version of "then"

Apologies if this isn't the best place to ask, but I'm having difficult understanding what's going on in this function, more specifically: why do we create a promise that's seemingly not used for anything? Thanks!

public func then(on queue: ExecutionContext = DispatchQueue.main, _ onFulfilled: @escaping (Value) -> (), _ onRejected: @escaping (Error) -> () = { _ in }) -> Promise<Value> {
_ = Promise<Value>(work: { fulfill, reject in
self.addCallbacks(
on: queue,
onFulfilled: { value in
fulfill(value)
onFulfilled(value)
},
onRejected: { error in
reject(error)
onRejected(error)
}
)
})
return self
}

How to switch catches on error types?

Thanks for writing #58, that's a nice feature, thanks!

I was looking for a way to mimick a switch-like decision tree with these error types. But from the looks of the current implementation, consecutive catch blocks will all be called when the promise is rejected:

afbeelding

(this test just serves to illustrate all three catch blocks are executed, sequentially)

There also seems to be no error-type based recover-method that would allow me to catch (and squelch) errors of a specific type, so if I want to be "picky" about specific error types, I still have to write the decision tree within the catch block, instead of leveraging the Promise API.

How would you approach something like this?
Cheers,
Eric-Paul

Require only App-Extension-Safe API

Im using this framework in an app-extension and get warning that it is not safe for use in application extension.

The fix is quite easy, changing the build setting Require Only App-Extension-Safe API to Yes solves it.

I also wonder why UIKit is imported in the umbrella-header file and not Foundation, where the FOUNDATION_EXPORT is declared and there seems to be no other dependency on UIKit?

Suggestion: map & flatMap?

I recall some issue with Swift's implicit typing and the overloading of then. It seems that because then is so overloaded, we end up having to explicitly type some closures.

I was thinking that it would help the type system and make our code more expressive if the then overloads were replaced with map and flatMap as appropriate.

One con for this change would be that it moves away from the A+ spec, but we aren't programming in javascript so I don't think that should be an issue. Another con would be that this would be a breaking change. Maybe both could be implemented with then deprecated for a version?

If this suggestion is acceptable, I would be happy to update the code... What say you?

What about 2.0.2 ?

There haven't been a lot of implementations' changes
There hasn't been a single API change
But still, there is 30+ commits to master since Feb, so I thought maybe it would be nice to bundle all the changes, especially getting that a public beta version of Xcode 10 is already out.

Suggestion: AllSettled implementation

Hi,

I've been using your library, and it's been fantastic so far. However, recently I came across a use case where I needed something like allSettled from JavaScript.

I came up with the following extension:

extension Promises {
    public static func allSettled<T>(_ promises: [Promise<T>]) -> Promise<[T?]> {
        return Promises.all(promises.map { promise in
            return Promise { fulfill, reject in
                promise.then { value in
                    fulfill(value)
                }.catch { _ in
                    fulfill(nil)
                }
            }
        })
    }
}

This uses optional types to resolve an array of promises to either their value, or nil if the promise fails.

Use Case

This can be useful if we need to wait for many actions to complete but don't care if some fail.

For instance, let's say we are loading 100 images for a social media feed. If some of the images fail to load, we don't want the entire operation to fail - we would rather display the images that did load, and place some placeholder for the ones that failed. This can easily be done with optional unwrapping, letting us deal with the error handling at a later stage.

The extension I wrote works well for me, but it would be nice to have something like this in the official API - my code is probably far from optimal anyway ๐Ÿ˜Š.

error: invalid target name at 'PromiseTests'; name of non-test targets cannot end in 'Tests'

Expected Behavior

  1. Clone khanlou/Promise
  2. Build khanlou/Promise
  3. Build library
  4. Link library against khanlou/Promise

Current Behavior

$ swift build
Updating https://github.com/khanlou/Promise.git
Cloning https://github.com/khanlou/Promise.git
Resolving https://github.com/khanlou/Promise.git at 2.0.1
error: invalid target name at 'PromiseTests'; name of non-test targets cannot end in 'Tests'

Steps to Reproduce

  1. mkdir new_project && cd new_project
  2. swift package init --type library && swift build && swift test
  3. in Package.swift add [ .package(url: "https://github.com/khanlou/Promise.git", from: "2.0.1"), ] without sq. brackets to the array of dependencies declared as " dependencies: [ ...... ] "
  4. swift build

Context (Environment)

swift-4.0.3-RELEASE-ubuntu16.10
Lubuntu 16.10 AMD64

Detailed Description

The issue makes impossible to build with and link against this library while maintaining project using Swift Package Manager. Was not tested on macOS.

Chaining unrelated network calls...

This is not the best place to ask the question, but the experts are here so here goes:

If I want to chain multiple network calls, how would I go about it? Each call has a different set of parameters and returns a Promise<[String:Any]>

Ideally, I would just store the results of each .then closure and simply return the next call:

  typealias GetPromise = Promise<[String:Any]>

  getDomains(baseDomain: credentials.baseDomain).then({ domains in
     self.domains = domains
     return self.getConfiguration(baseDomain: credentials.baseDomain, brandId: credentials.brandId)
  }).then({ configuration in
     self.configuration = configuration
     return self.getServerTime(baseDomain: credentials.baseDomain)
  }).then({ serverTime in
     self.serverTime = serverTime
     resultsBlock(Results.success)
  }).catch { (error) in
     resultsBlock(Results.error(error))
  }

Unfortunately that violates the non-void return value in voice function rule.

So then I tried to return the results of each call with the promise in the next as a tuple, but that caused destructuring issues:

  typealias GetPromise = Promise<[String:Any]>

  getDomains(baseDomain: credentials.baseDomain).then({ domains in
     return (domains, self.getConfiguration(baseDomain: credentials.baseDomain, brandId: credentials.brandId))
  }).then({ (domains, configuration) in
     return (domains, configuration, self.getServerTime(baseDomain: credentials.baseDomain))
  }).then({ (domains, configuration, serverTime) in
     resultsBlock(Results.succes)
  }).catch { (error) in
     resultsBlock(Results.error(error))
  }

Here are my method definitions:

   private func getDomains(baseDomain: String) -> GetPromise {
	  return get(urlPath: discoveryPath)
   }

   private func getConfiguration(baseDomain: String, brandId: String) -> GetPromise {
	  return get(urlPath: configurationPath)
   }

   private func getServerTime(baseDomain: String) -> GetPromise {
	  return get(urlPath: serverTimePath)
   }

   private func get(urlPath: String) -> GetPromise {
	  return GetPromise(work: { fulfill, reject in
		 guard let url = URL(string: urlPath) else {
			reject(ConnectionManager.basicError)
			return
		 }
	 
		 let task = URLSession.shared.dataTask(with: url) {(data, _, error) in
			if let error = error {
			   reject(error)
			   return
			}
		
			if let data = data
			   , let jsonObject = try? JSONSerialization.jsonObject(with: data, options: [])
			   , let dictValue = jsonObject as? [String:Any] {
			   fulfill(dictValue)
			   return
			}
		
			reject(ConnectionManager.basicError)
		 }
	 
		 task.resume()
	  })
   }

Any ideas?

Promise chaining after `catch` is lacking

then clause comes in 3 different variants

public func then<NewValue>(on queue: ExecutionContext = default, _ onFulfilled: @escaping (Value) throws -> Promise.Promise<NewValue>) -> Promise.Promise<NewValue>

public func then<NewValue>(on queue: ExecutionContext = default, _ onFulfilled: @escaping (Value) throws -> NewValue) -> Promise.Promise<NewValue>

public func then(on queue: ExecutionContext = default, _ onFulfilled: @escaping (Value) -> (), _ onRejected: @escaping (Error) -> () = default) -> Promise.Promise<Value>

This allows for promise chaining, one of the core concepts of promises, a feature of Javascript promises.

However, for some reason there is no such option for catch clauses and there is only one variant

public func `catch`(on queue: ExecutionContext = default, _ onRejected: @escaping (Error) -> ()) -> Promise.Promise<Value>

What is the opinion about this functionality? Seems pretty core for me, should be added at some point. @khanlou what can you say?

Incorrect example code in Readme

In the the examples under Ensure and Throwing you write: URLSession.shared.dataTask(with: request) , but the dataTask itself is not a promise. Did you mean to use the data task wrapping promise created under Creating Promises?

promise
  .ensure({ data, httpResponse in
      return httpResponse.statusCode == 200
  })
  .then({ data, httpResponse in
      // the statusCode is valid
  })
  .catch({ error in 
      // the network request failed, or the status code was invalid
  })

Update Podspec

Would it be possible to get a new release added on so that the pod repo is up to date with some of the latest changes? The current iteration shows a compiler warning in later versions of swift due to compactMap which has been fixed in the repo but not pushed out. Thanks!

Race condition when chaining a promise after being fulfilled?

Hi @khanlou - first of all, thanks for all the effort putting this library together!

I've come across a potential issue which seems like a race condition inside the library. Here is code for a test:

class PromiseTests: XCTestCase {
    
    func testChainingOntoFulfilledPromiseWithOutstandingCallbacks() {
        let promise = Promise<String>()
        var didCallAlways = false
        let thenExpectation = expectation(description: "Did call 'then'")
        let alwaysExpectation = expectation(description: "Did call 'always'")
        
        promise.catch { error in
            XCTAssertEqual(didCallAlways, false)
        }.then { value in
            XCTAssertEqual(didCallAlways, false)
            thenExpectation.fulfill()
        }
        
        promise.fulfill("Hello World")
        promise.always {
            didCallAlways = true
            alwaysExpectation.fulfill()
        }
        
        wait(for: [thenExpectation, alwaysExpectation])
    }
    
}

Which intermittently fails inside

.then { value in
            XCTAssertEqual(didCallAlways, false)
            thenExpectation.fulfill()
        }

on

XCTAssertEqual(didCallAlways, false)

So, the scenario here is chaining something to the end of a fulfilled promise which has outstanding tasks chained on the end. From what I can understand stepping through the source code, I think the order in which the the "catch" & "then" tasks vs the "always" task executes is not guaranteed.

My assumption in this case was that the "always" should be queued behind the outstanding "catch" and "then" tasks.

I'm not sure if you'd consider this a bug, or a misuse of the API or something else, so thought I would raise it for discussion

Cheers

Callback threading problems

Hi,

I just discovered an issue with the callbacks. Please run my test a couple of times in order to see what's going on (sometimes it'll fail, but sometimes it won't):

func testQueues() {
    var then: DispatchTime!
    var always: DispatchTime!
    weak var expectation = self.expectation(description: "Threading is hard!")
    Promise(value: 1)
    .then(on: DispatchQueue.global(qos: .default), { value in
        return 2
    })
    .then(on: DispatchQueue.global(qos: .background)) { value -> Promise<Int> in
        return Promise<Int>(queue: .main) { s, f in
            DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) {
                s(3)
            }
        }
    }
    .then(on: DispatchQueue.global(qos: .background)) { value in
        return 4
    }
    .then(on: DispatchQueue.global(qos: .utility), { value in
        then = DispatchTime.now()
    })
    .catch(on: DispatchQueue.global(qos: .default)) { err in
        // do nothing, won't fail
    }
    .then(on: DispatchQueue.global(qos: .default)) { value in
        always = DispatchTime.now()
        XCTAssert(then != nil)
        XCTAssert(always > then)
        expectation?.fulfill()
    }
    waitForExpectations(timeout: 5, handler: nil)
}

The source of this problem is here:

private func fireCallbacksIfCompleted() {
    lockQueue.async(execute: {
        guard !self.state.isPending else { return }
        self.callbacks.forEach { callback in
            switch self.state {
            case let .fulfilled(value):
                callback.callFulfill(value)
            case let .rejected(error):
                callback.callReject(error)
            default:
                break
            }
        }
        self.callbacks.removeAll()
    })
}

This method won't wait for the previous callback to finish, so if the actual execution is on multiple threads, the callback order can be messed up. It's a pretty nasty bug. ๐Ÿ›๐Ÿ™ƒ

I'd suggest to use a DispatchGroup so you could get notified after a callback finished it's work. Also If you have a better alternative in order to fix this, please let me know. Thanks! ๐Ÿ‘

Not sure how to return Void promise

Given:

    func verify(userId: String, withToken: String) -> Promise<Void> {
        Promise<Void>(work: { [self] fulfill, reject in
            let networking = makeClient(headers: ["X-Application-Publishable-Key" : apiKey])
            networking.post("/api/users/\(userId)/verify/", parameters: ["token" : withToken]) { result in
                switch result {
                case .success(let response):
                    fulfill() <---- PROBLEM HERE
                case .failure(let response):
                    //unpack the error etc 
                    reject(clientError)
                }
            }
        })
    }

Results in: Missing argument for parameter #1 in call. Did I miss something? Just coming back to Swift after dabbling in other languages.

macOS deployment target

The macOS deployment target is unset and seems to default to 10.13. Iโ€™m trying to integrate the framework via Carthage and got a compile error because my app has a 10.11 deployment target. Are you aware of this issue or maybe Iโ€™m doing something wrong. What would the minimum deployment target on Mac be? I guess the minimum version Swift 3.2 runs on. I could not find any info about this.

macOS target does not include source files

The target membership is not set on the Promise.swift and Promise+Extras.swift files. I had some issues integrating the framework via Carthage and this was one of the things I did to fix it. Makes sense to me that the files need the target membership for the mac library to work, but I might be missing something.

Support joining Promises of different types

Promise.all only works with Promises of one type. It would be nice to have similar helpers for Promises of different types, too. I think the only way to accomplish it is to have a separate method for each arity. I didn't know if you're interested in including a set of methods like that, so I decided to start by filing an issue instead of going directly for a PR.

I solved it like this:

    public static func join<T1, T2>(_ p1: Promise<T1>, _ p2: Promise<T2>) -> Promise<(T1, T2)> {
        return Promise<(T1, T2)>(work: { fulfill, reject in
            p1.then({ value in
                if let p2val = p2.value {
                    fulfill((value, p2val))
                }
            }).catch(reject)
            p2.then({ value in
                if let p1val = p1.value {
                    fulfill((p1val, value))
                }
            }).catch(reject)
        })
    }

    public static func join<T1, T2, T3>(_ p1: Promise<T1>, _ p2: Promise<T2>, _ last: Promise<T3>) -> Promise<(T1, T2, T3)> {
        return Promise<(T1, T2, T3)>(work: { fulfill, reject in
            let joined = join(p1, p2)

            func callFulfill(_ joinedVal: (T1, T2), _ lastVal: T3) -> Void {
                fulfill((joinedVal.0, joinedVal.1, lastVal))
            }

            joined.then({ value in
                if let lastval = last.value {
                    callFulfill(value, lastval)
                }
            }).catch(reject)
            last.then({ value in
                if let jval = joined.value {
                    callFulfill(jval, value)
                }
            }).catch(reject)
        })
    }

I generated the three parameter version with this:

func types(_ n: Int) -> String {
    return (1...n).map { "T\($0)" }.joined(separator: ", ")
}


func createJoin(_ n: Int) -> String {
    var output: String = "public static func join<"
    output.append(types(n))
    output.append(">(")
    output.append((1...n-1).map { "_ p\($0): Promise<T\($0)>, " }.joined())
    output.append("_ last: Promise<T\(n)>) -> Promise<(\(types(n)))> {\n")

    output.append("return Promise<(\(types(n)))>(work: { fulfill, reject in\n")

    output.append("let joined = join(")
    output.append((1...n-1).map { "p\($0)" }.joined(separator: ", "))
    output.append(")\n\n")

    output.append("func callFulfill(_ joinedVal: (\(types(n-1))), _ lastVal: T\(n)) -> Void {\n")

    output.append("fulfill((")
    output.append((0...n-2).map { "joinedVal.\($0), " }.joined())
    output.append("lastVal))\n")
    output.append("}\n\n")

    ["joined.then({ value in",
     "if let lastval = last.value {",
     "callFulfill(value, lastval)",
     "}",
     "}).catch(reject)",
     "last.then({ value in",
     "if let jval = joined.value {",
     "callFulfill(jval, value)",
     "}",
     "}).catch(reject)"].forEach { line in
        output.append(line)
        output.append("\n")
    }

    output.append("})\n}")

    return output
}

print(createJoin(3))

So you could use that to generate methods for any arity N โ‰ฅ 3, as long as you also had generated ones for 3โ€ฆN.

Should I file a pull request with something based on this?

As of #43, the project might be incompatible with Xcode prior to 9.3 GM and Swift prior to 4.1

The issue is discussed in commets to #43 . I will attempt to confirm this behavior today by attempting to compile master with the older toolchain. However the issue has been observed by the original PR creator when Travis said that there is no such function as compactMap since it's been added in Swift 4.1's stdlib. Overall, the #43 has essentially destroyed all the backwards compatibility that there could be.

Conflict with State enum and @State in SwiftUI

I am playing around with SwiftUI and stated getting the error

Generic enum 'State' cannot be used as an attribute.

on the @State property wrapper due to an enum called State in the Promise library.
I had to distinguish the property wrapper by naming it @SwiftUI.State to make the error go away.

Tests while in SPM

The test target is not configured in Swift Package Manager environment.
Furthermore, there is a certain degree of suspicion that Tests wouldn't even build for Linux because of not yet implemented parts of Swift foundation (not Foundation framework, but whole arrangement of tools and libs) being used... Possibly.

Make a tag?

Would be cool to make a tag (maybe v0.1) so I could grab the code via Carthage!

Chaining problem with Promise<Any>

This looks like a swift compiler method matching bug, but a promise holding Any (e.g. used for JSON parsing) will not be used with the flatMap but the map variant of the then method, resulting in the promise object itself being treated as the value. See this simple snippet that can be pasted into a test class:

    func promiseAny() -> Promise<Any> {
        return Promise<Any> { fulfill, _ in
            fulfill("a")
        }
    }
    
    func testMatch() {
        let exp = expectation(description: "")
        Promise<Int>(value: 1).then { _ in
            return self.promiseAny()
        }.then { value in
            XCTAssert(value == "a")
            exp.fulfill()
        }
        waitForExpectations(timeout: 1, handler: nil)
    }

value == "a" does not compile because the compiler thinks value is of type Promise<Any> where it should be Any in my opinion. Changing the promiseAny function to return Promise<String> makes it work, though.

Maybe this is a known swift issue or maybe this is how it should work and I have it backwards, but I think this could be pointed out in the README. Or maybe you even know a way to fix it :)

Otherwise thanks for this great library. I looked at some other swift promises libs today and only this one works the way I expected it to.

TravisCI making it look like it works

TravisCI is as commonly known doesn't work with Swift because of some issues with multi targeted Objective-C builds in Xcode or whatever...
And also it has come to my attention that in 7b9764c the Travis badge has been removed altogether, which makes me think that its absence is commonly tolerated and accepted :)

Suggestion: @khanlou should log in at public https://travis-ci.org and disable automatic builds for this repository altogether, so it doesn't show that build failed for literally every single commit and pull-request.

Catch Clause Not Invoked

I've set up a somewhat complex Promise chain where the catch clause is not invoked as expected.

Here's a test-case for Promise (fails) and another for PromiseKit (passes).

Test case is derived from a real world app scenario that didn't work as I expected it would. Perhaps it is a user error or misunderstanding.

Failing Test:

import Foundation
import Promise

class Chain {

    func verify(userId: String, withToken: String) -> Promise<Void> {
        chunk1()
            .then { [self] result in
                chunk2()
            }
            .then {
                print("All done!")
            }
    }

    func chunk1() -> Promise<String> {
        first()
            .then { [self] result in
                second()
            }
            .then { [self] result in
                third()
            }
            .then { [self] result in
                fourth()
            }
    }

    func chunk2() -> Promise<String> {
        fifth()
            .then { [self] result in
                sixth()
            }
            .then { [self] result in
                seventh()
            }
            .then { [self] result in
                eighth()
            }
    }

    func first() -> Promise<String> {
        Promise<String>(work: { fulfill, reject in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 1"
                print(message)
                fulfill(message)
            }
        })
    }

    func second() -> Promise<String> {
        Promise<String>(work: { fulfill, reject in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 2"
                print(message)
                fulfill(message)
            }
        })
    }

    func third() -> Promise<String> {
        Promise<String>(work: { fulfill, reject in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 3"
                print(message)
                fulfill(message)
            }
        })
    }

    func fourth() -> Promise<String> {
        Promise<String>(work: { fulfill, reject in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 4"
                print(message)
                fulfill(message)
            }
        })
    }

    func fifth() -> Promise<String> {
        Promise<String>(work: { fulfill, reject in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 5"
                print(message)
                fulfill(message)
            }
        })
    }

    func sixth() -> Promise<String> {
        Promise<String>(work: { fulfill, reject in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 6"
                print(message)
                reject(RuntimeError(reason: "bang at 6"))
            }
        })
    }

    func seventh() -> Promise<String> {
        Promise<String>(work: { fulfill, reject in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 7"
                print(message)
                fulfill(message)
            }
        })
    }

    func eighth() -> Promise<String> {
        Promise<String>(work: { fulfill, reject in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 8"
                print(message)
                fulfill(message)
            }
        })
    }

    func ninth() -> Promise<String> {
        Promise<String>(work: { fulfill, reject in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 9"
                print(message)
                fulfill(message)
            }
        })
    }

}

Failing test:

class PromiseTest : XCTestCase {


    func test_it_handles_chain() {

        var result: Bool? = nil

        let chain = Chain()

        chain.verify(userId: "foo", withToken: "foo")
            .then {
                result = false
            }
            .catch { error in
                result = true
            }


        expect {
            result != nil
        }

        print("Go the final result: \(result)")
        XCTAssertEqual(result, true)

    }

}

PromiseKit Version

class PKChain {

    func verify(userId: String, withToken: String) -> Promise<Void> {
        chunk1()
            .then { [self] result in
                chunk2()
            }
            .done { result in
                print("All done!")
            }
    }

    func chunk1() -> Promise<String> {
        first()
            .then { [self] result in
                second()
            }
            .then { [self] result in
                third()
            }
            .then { [self] result in
                fourth()
            }
    }

    func chunk2() -> Promise<String> {
        fifth()
            .then { [self] result in
                sixth()
            }
            .then { [self] result in
                seventh()
            }
            .then { [self] result in
                eighth()
            }
    }

    func first() -> Promise<String> {
        Promise { seal in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 1"
                print(message)
                seal.fulfill(message)
            }
        }
    }

    func second() -> Promise<String> {
        Promise { seal in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 2"
                print(message)
                seal.fulfill(message)
            }
        }
    }

    func third() -> Promise<String> {
        Promise { seal in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 3"
                print(message)
                seal.fulfill(message)
            }
        }
    }

    func fourth() -> Promise<String> {
        Promise { seal in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 4"
                print(message)
                seal.fulfill(message)
            }
        }
    }

    func fifth() -> Promise<String> {
        Promise { seal in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 5"
                print(message)
                seal.fulfill(message)
            }
        }
    }

    func sixth() -> Promise<String> {
        Promise { seal in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 6"
                print(message)
                seal.reject(RuntimeError(reason: "bang at 6"))
            }
        }
    }

    func seventh() -> Promise<String> {
        Promise { seal in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 7"
                print(message)
                seal.fulfill(message)
            }
        }
    }

    func eighth() -> Promise<String> {
        Promise { seal in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 8"
                print(message)
                seal.fulfill(message)
            }
        }
    }

    func ninth() -> Promise<String> {
        Promise { seal in
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                let message = "message 9"
                print(message)
                seal.fulfill(message)
            }
        }
    }

}

Test case:

class PKPromisesTest : XCTestCase {

    func test_it_handles_chain() {

        var result: Bool? = nil

        let chain = PKChain()

        chain.verify(userId: "foo", withToken: "foo")
            .done { string in
                result = false
            }
            .catch { error in
                print("Ahahahahah error: \(error)")
                result = true
            }


        expect {
            result != nil
        }

        print("Go the final result: \(result)")
        XCTAssertEqual(result, true)

    }

}

Source for Expect/Async Function:

(Sometimes I find XCTest style expectations fussy, so I roll this one:)

func expect(condition: @escaping () -> Bool) {
    expectWithin(seconds: 7, condition: condition)
}

func expectWithin(seconds: TimeInterval, condition: @escaping () -> Bool) {
    for _ in 0..<Int(seconds) {
        if condition() {
            return
        } else {
            RunLoop.current.run(until: Date(timeIntervalSinceNow: 1))
        }
    }
    fatalError("Condition didn't happen before timeout:\(seconds)")
}

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.