Skip to content

Commit

Permalink
Adds all() that accepts 4 heterogenous values (#62)
Browse files Browse the repository at this point in the history
Fixes #61
  • Loading branch information
EthanLozano authored and temrich committed Jul 26, 2018
1 parent 1e15edb commit 8fd7f28
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 0 deletions.
48 changes: 48 additions & 0 deletions Sources/Promises/Promise+All.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,51 @@ public func all<A, B, C>(
}
return promise
}

/// Waits until all of the promises have been fulfilled.
/// If one of the promises is rejected, then the returned promise is rejected with same error.
/// If any other arbitrary value or `Error` appears in the array instead of `Promise`,
/// it's implicitly considered a pre-fulfilled or pre-rejected `Promise` correspondingly.
/// - parameters:
/// - queue: A queue to dispatch on.
/// - promiseA: Promise of type `A`.
/// - promiseB: Promise of type `B`.
/// - promiseC: Promise of type `C`.
/// - promiseD: Promise of type `D`.
/// - returns: Promise of a tuple containing the values of input promises in the same order.
public func all<A, B, C, D>(
on queue: DispatchQueue = .promises,
_ promiseA: Promise<A>,
_ promiseB: Promise<B>,
_ promiseC: Promise<C>,
_ promiseD: Promise<D>
) -> Promise<(A, B, C, D)> {
let promises = [
promiseA.objCPromise,
promiseB.objCPromise,
promiseC.objCPromise,
promiseD.objCPromise
]
let promise = Promise<(A, B, C, D)>(
Promise<(A, B, C, D)>.ObjCPromise<AnyObject>.__onQueue(
queue,
all: promises
).__onQueue(queue, then: { objCValues in
guard let values = objCValues as [AnyObject]?,
let valueA = Promise<A>.asValue(values[0]),
let valueB = Promise<B>.asValue(values[1]),
let valueC = Promise<C>.asValue(values[2]),
let valueD = Promise<D>.asValue(values[3])
else {
preconditionFailure("Cannot convert \(type(of: objCValues)) to \((A, B, C, D).self)")
}
return (valueA, valueB, valueC, valueD)
})
)
// Keep Swift wrapper alive for chained promises until `ObjCPromise` counterpart is resolved.
promises.forEach {
$0.__pendingObjects?.add(promise)
}
return promise
}

111 changes: 111 additions & 0 deletions Tests/PromisesTests/Promise+AllTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -316,4 +316,115 @@ class PromiseAllTests: XCTestCase {
XCTAssertNil(weakExtendedPromise1)
XCTAssertNil(weakExtendedPromise2)
}

func testPromiseAllHeterogeneous4() {
// Arrange.
let expectedValues = (42, "hello world", Int?.none, 2.4)
let promise1 = Promise<Int> { fulfill, _ in
Test.delay(0.1) {
fulfill(42)
}
}
let promise2 = Promise<String> { fulfill, _ in
Test.delay(1) {
fulfill("hello world")
}
}
let promise3 = Promise<Int?> { fulfill, _ in
Test.delay(2) {
fulfill(nil)
}
}
let promise4 = Promise<Double> { fulfill, _ in
Test.delay(3) {
fulfill(2.4)
}
}

// Act.
let combinedPromise = all(promise1, promise2, promise3, promise4).then { number, string, none, double in
XCTAssert(number == expectedValues.0)
XCTAssert(string == expectedValues.1)
XCTAssert(none == expectedValues.2)
XCTAssert(double == expectedValues.3)
}

// Assert.
XCTAssert(waitForPromises(timeout: 10))
guard let value = combinedPromise.value else { XCTFail(); return }
XCTAssert(value.0 == expectedValues.0)
XCTAssert(value.1 == expectedValues.1)
XCTAssert(value.2 == expectedValues.2)
XCTAssert(value.3 == expectedValues.3)
XCTAssertNil(combinedPromise.error)
}

func testPromiseAllHeterogeneous4Reject() {
// Arrange.
let promise1 = Promise { fulfill, _ in
Test.delay(0.1) {
fulfill(42)
}
}
let promise2 = Promise<String> { _, reject in
Test.delay(1) {
reject(Test.Error.code42)
}
}
let promise3 = Promise<Int?> { fulfill, _ in
Test.delay(2) {
fulfill(nil)
}
}
let promise4 = Promise<Double> { fulfill, _ in
Test.delay(3) {
fulfill(2.4)
}
}

// Act.
let combinedPromise = all(promise1, promise2, promise3, promise4).then { _ in
XCTFail()
}.catch { error in
XCTAssertTrue(error == Test.Error.code42)
}

// Assert.
XCTAssert(waitForPromises(timeout: 10))
XCTAssertTrue(combinedPromise.error == Test.Error.code42)
XCTAssertNil(combinedPromise.value)
}

func testPromiseAllHeterogeneous4NoDeallocUntilResolved() {
// Arrange.
let promise1 = Promise<Int>.pending()
let promise2 = Promise<String>.pending()
let promise3 = Promise<Int?>.pending()
let promise4 = Promise<Double>.pending()
weak var weakExtendedPromise1: Promise<(Int, String, Int?, Double)>?
weak var weakExtendedPromise2: Promise<(Int, String, Int?, Double)>?

// Act.
autoreleasepool {
XCTAssertNil(weakExtendedPromise1)
XCTAssertNil(weakExtendedPromise2)
weakExtendedPromise1 = all(promise1, promise2, promise3, promise4)
weakExtendedPromise2 = all(promise1, promise2, promise3, promise4)
XCTAssertNotNil(weakExtendedPromise1)
XCTAssertNotNil(weakExtendedPromise2)
}

// Assert.
XCTAssertNotNil(weakExtendedPromise1)
XCTAssertNotNil(weakExtendedPromise2)

promise1.fulfill(42)
promise2.fulfill("hello world")
promise3.fulfill(nil)
promise4.fulfill(2.4)
XCTAssert(waitForPromises(timeout: 10))

XCTAssertNil(weakExtendedPromise1)
XCTAssertNil(weakExtendedPromise2)
}
}

0 comments on commit 8fd7f28

Please sign in to comment.