Skip to content

Commit

Permalink
Merge pull request #1269 from RomanPodymov/feature/race_fulfilled
Browse files Browse the repository at this point in the history
func race(fulfilled:) for Objective-C
  • Loading branch information
mxcl authored Sep 24, 2021
2 parents a7ac96a + 6c4b01b commit 2ff9793
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 0 deletions.
6 changes: 6 additions & 0 deletions Sources/fwd.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ extern NSString * __nonnull const PMKErrorDomain;
#define PMKOperationFailed 8l
#define PMKTaskError 9l
#define PMKJoinError 10l
#define PMKNoWinnerError 11l


#ifdef __cplusplus
Expand Down Expand Up @@ -160,6 +161,11 @@ extern AnyPromise * __nonnull dispatch_promise_on(dispatch_queue_t __nonnull que
*/
extern AnyPromise * __nonnull PMKRace(NSArray * __nonnull promises) NS_REFINED_FOR_SWIFT;

/**
Returns a new promise that resolves with the value of the first fulfilled promise in the provided array of promises.
*/
extern AnyPromise * __nonnull PMKRaceFulfilled(NSArray * __nonnull promises) NS_REFINED_FOR_SWIFT;

#ifdef __cplusplus
} // Extern C
#endif
33 changes: 33 additions & 0 deletions Sources/race.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
#import "AnyPromise+Private.h"
#import <libkern/OSAtomic.h>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// ^^ OSAtomicDecrement32 is deprecated on watchOS

AnyPromise *PMKRace(NSArray *promises) {
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
Expand All @@ -7,3 +12,31 @@
}
}];
}

/**
Waits for one promise to fulfill
@note If there are no fulfilled promises, the returned promise is rejected with `PMKNoWinnerError`.
@param promises The promises to fulfill.
@return The promise that was fulfilled first.
*/
AnyPromise *PMKRaceFulfilled(NSArray *promises) {
__block int32_t countdown = (int32_t)[promises count];

return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
for (__strong AnyPromise* promise in promises) {
[promise __pipe:^(id value) {
if (IsError(value)) {
if (OSAtomicDecrement32(&countdown) == 0) {
id err = [NSError errorWithDomain:PMKErrorDomain code:PMKNoWinnerError userInfo:@{NSLocalizedDescriptionKey: @"PMKRaceFulfilled(nil)"}];
resolve(err);
}
} else {
resolve(value);
}
}];
}
}];
}

#pragma GCC diagnostic pop
38 changes: 38 additions & 0 deletions Tests/CoreObjC/AnyPromiseTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,44 @@ - (void)test_race {
[self waitForExpectationsWithTimeout:1 handler:nil];
}

- (void)test_race_fullfilled {
id ex = [self expectationWithDescription:@""];
NSArray* promises = @[
PMKAfter(1).then(^{ return dummyWithCode(1); }),
PMKAfter(2).then(^{ return dummyWithCode(2); }),
PMKAfter(5).then(^{ return @1; }),
PMKAfter(4).then(^{ return @2; }),
PMKAfter(3).then(^{ return dummyWithCode(3); })
];
PMKRaceFulfilled(promises).then(^(id obj){
XCTAssertEqual(2, [obj integerValue]);
[ex fulfill];
}).catch(^{
XCTFail();
[ex fulfill];
});
[self waitForExpectationsWithTimeout:10 handler:nil];
}

- (void)test_race_fullfilled_with_no_winner {
id ex = [self expectationWithDescription:@""];
NSArray* promises = @[
PMKAfter(1).then(^{ return dummyWithCode(1); }),
PMKAfter(2).then(^{ return dummyWithCode(2); }),
PMKAfter(3).then(^{ return dummyWithCode(3); })
];
PMKRaceFulfilled(promises).then(^(id obj){
XCTFail();
[ex fulfill];
}).catch(^(NSError *e){
XCTAssertEqual(e.domain, PMKErrorDomain);
XCTAssertEqual(e.code, PMKNoWinnerError);
XCTAssertEqualObjects(e.userInfo[NSLocalizedDescriptionKey], @"PMKRaceFulfilled(nil)");
[ex fulfill];
});
[self waitForExpectationsWithTimeout:10 handler:nil];
}

- (void)testInBackground {
id ex = [self expectationWithDescription:@""];
PMKAfter(0.1).thenInBackground(^{ [ex fulfill]; });
Expand Down

0 comments on commit 2ff9793

Please sign in to comment.