From 7ec651701a87a7fca5154f5b505cbe20739050e8 Mon Sep 17 00:00:00 2001 From: yaochenfeng <282696845@qq.com> Date: Mon, 9 Jun 2025 20:55:50 +0800 Subject: [PATCH 1/6] feat: update promise --- Sources/DFService/ServicePromise.swift | 238 ++++++++----- .../DFServiceTests/ServicePromiseTests.swift | 325 ++++++++++-------- 2 files changed, 340 insertions(+), 223 deletions(-) diff --git a/Sources/DFService/ServicePromise.swift b/Sources/DFService/ServicePromise.swift index 35a7398..5bdca14 100644 --- a/Sources/DFService/ServicePromise.swift +++ b/Sources/DFService/ServicePromise.swift @@ -2,18 +2,22 @@ public final class ServicePromise { public typealias Resolve = (Value) -> Void public typealias Reject = (Error) -> Void public enum State { - case pending, fulfilled, rejected, cancelled + case pending, fulfilled, rejected } - private var successHandler: ((Value) -> Void)? - private var failureHandler: ((Error) -> Void)? - private var finalHandler: (() -> Void)? - private var cancelHandler: (() -> Void)? + // private var successHandler: ((Value) -> Void)? + // private var failureHandler: ((Error) -> Void)? + // private var finalHandler: (() -> Void)? + // private var cancelHandler: (() -> Void)? + /// 用于存储成功回调的数组,便于在 Promise 被解析时调用 + private var onSuccessHandlers: [(Value) -> Void] = [] + /// 用于存储失败回调的数组,便于在 Promise 被拒绝时调用 + private var onFailureHandlers: [(Error) -> Void] = [] + /// 用于存储最终回调的数组,便于在 Promise 完成时调用 + private var onFinallyHandlers: [() -> Void] = [] - private(set) public var state: State = .pending - private var result: Result? - private var isResolved = false - private var isCancelled = false + public private(set) var state: State = .pending + public private(set) var result: Result? public init( _ executor: @escaping (_ resolve: @escaping Resolve, _ reject: @escaping Reject) -> Void @@ -22,116 +26,177 @@ public final class ServicePromise { } public func resolve(_ value: Value) { - guard !isResolved && !isCancelled else { return } - isResolved = true + guard state == .pending else { return } state = .fulfilled result = .success(value) - successHandler?(value) - finalHandler?() + self.onSuccessHandlers.forEach { $0(value) } + self.onFinallyHandlers.forEach { $0() } + clearCallbacks() } public func reject(_ error: Error) { - guard !isResolved && !isCancelled else { return } - isResolved = true + guard state == .pending else { return } state = .rejected result = .failure(error) - failureHandler?(error) - finalHandler?() + self.onFailureHandlers.forEach { $0(error) } + self.onFinallyHandlers.forEach { $0() } + clearCallbacks() } - @discardableResult - public func then(_ handler: @escaping (Value) -> Void) -> Self { - successHandler = handler - if case .success(let value)? = result { - handler(value) - } - return self + private func clearCallbacks() { + onSuccessHandlers.removeAll() + onFailureHandlers.removeAll() + onFinallyHandlers.removeAll() } +} +extension ServicePromise { @discardableResult - public func `catch`(_ handler: @escaping (Error) -> Void) -> Self { - failureHandler = handler - if case .failure(let error)? = result { - handler(error) - } - return self - } + public func then(_ onFulfilled: @escaping (Value) throws -> U) -> ServicePromise { + return ServicePromise { resolve, reject in + let handle = { (value: Value) in + do { + let result = try onFulfilled(value) + resolve(result) + } catch { + reject(error) + } + } - @discardableResult - public func finally(_ handler: @escaping () -> Void) -> Self { - finalHandler = handler - if result != nil { - handler() + switch self.state { + case .fulfilled: + if case .success(let value) = self.result { + handle(value) + } + case .rejected: + if case .failure(let error) = self.result { + reject(error) + } + case .pending: + self.onSuccessHandlers.append(handle) + self.onFailureHandlers.append(reject) + } } - return self - } - - public func cancel() { - guard !isResolved && !isCancelled else { return } - isCancelled = true - state = .cancelled - cancelHandler?() } - @discardableResult - public func onCancel(_ handler: @escaping () -> Void) -> Self { - cancelHandler = handler - return self - } -} - -extension ServicePromise { + public func then( + _ onFulfilled: @escaping (Value) throws -> ServicePromise + ) -> ServicePromise { + return ServicePromise { resolve, reject in + let handle: (Value) -> Void = { value in + do { + let nextPromise = try onFulfilled(value) + nextPromise.then { innerValue in + resolve(innerValue) + }.catch { error in + reject(error) + } + } catch { + reject(error) + } + } - /// 转换当前 Promise 的结果为新的值类型 - @discardableResult - public func map(_ transform: @escaping (Value) -> T) -> ServicePromise { - return ServicePromise { resolve, reject in - self.then { value in - resolve(transform(value)) - }.catch { error in - reject(error) + switch self.state { + case .fulfilled: + if case .success(let value) = self.result { + handle(value) + } + case .rejected: + if case .failure(let error) = self.result { + reject(error) + } + case .pending: + self.onSuccessHandlers.append(handle) + self.onFailureHandlers.append(reject) } } } - /// 扁平化嵌套 Promise,适合返回另一个异步任务 @discardableResult - public func flatMap(_ transform: @escaping (Value) -> ServicePromise) -> ServicePromise - { - return ServicePromise { resolve, reject in - self.then { value in - let next = transform(value) - next.then(resolve).catch(reject) - }.catch { error in - reject(error) + public func `catch`(_ onRejected: @escaping (Error) -> Void) -> Self { + switch self.state { + case .fulfilled: + break + case .rejected: + if case .failure(let error) = self.result { + onRejected(error) } + case .pending: + self.onFailureHandlers.append(onRejected) } + return self + } + @discardableResult + public func finally(_ onFinally: @escaping () -> Void) -> Self { + switch self.state { + case .fulfilled, .rejected: + onFinally() + case .pending: + self.onFinallyHandlers.append(onFinally) + } + return self } - /// 等待多个 Promise 完成,返回一个新的 Promise 包含所有结果 - public static func all(_ promises: [ServicePromise]) -> ServicePromise<[T]> { - return ServicePromise<[T]> { resolve, reject in - var results = [T?](repeating: nil, count: promises.count) + //拓展Promise 常用函数 转换错误等 + public static func all(_ promises: [ServicePromise]) -> ServicePromise<[Value]> { + return ServicePromise<[Value]> { resolve, reject in + var results: [Value] = [] var remaining = promises.count - for (i, promise) in promises.enumerated() { + if remaining == 0 { + resolve([]) + return + } + + for promise in promises { promise.then { value in - results[i] = value + results.append(value) remaining -= 1 if remaining == 0 { - resolve(results.compactMap { $0 }) + resolve(results) } - }.catch(reject) + }.catch { error in + reject(error) + } } } } - // 异步构造:从 async 函数创建 ServicePromise - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public static func fromAsync(_ work: @escaping () async throws -> Value) -> ServicePromise< - Value - > { - return ServicePromise { resolve, reject in + public static func resolve(_ value: Value) -> ServicePromise { + return ServicePromise { resolve, _ in + resolve(value) + } + } + + public static func reject(_ error: Error) -> ServicePromise { + return ServicePromise { _, reject in + reject(error) + } + } + + public var value: Value? { + switch self.result { + case .success(let value): + return value + case .failure: + return nil + case nil: + return nil + } + } + + public var isPending: Bool { + return self.state == .pending + } + +} + +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) +extension ServicePromise { + public convenience init( + _ work: @escaping () async throws -> Value + ) { + self.init { resolve, reject in Task { do { let result = try await work() @@ -142,11 +207,7 @@ extension ServicePromise { } } } - - /// 将当前 ServicePromise 转换为异步函数,适用于 Swift 5.5+ 的 async/await - /// 注意:此方法需要在支持 async/await 的环境中使用 - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public func toAsync() async throws -> Value { + public func wait() async throws -> Value { return try await withCheckedThrowingContinuation { continuation in self.then { value in continuation.resume(returning: value) @@ -155,5 +216,4 @@ extension ServicePromise { } } } - } diff --git a/Tests/DFServiceTests/ServicePromiseTests.swift b/Tests/DFServiceTests/ServicePromiseTests.swift index 3c03879..c106730 100644 --- a/Tests/DFServiceTests/ServicePromiseTests.swift +++ b/Tests/DFServiceTests/ServicePromiseTests.swift @@ -11,209 +11,266 @@ import XCTest final class ServicePromiseTests: XCTestCase { - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - func testPromiseResolvesSuccessfully() { - let expectation = self.expectation(description: "Promise should resolve") - let promise = ServicePromise { resolve, _ in - resolve(10) + func testPromiseResolvesImmediately() { + let expectation = self.expectation(description: "Immediate resolve") + let promise = ServicePromise { resolve, _ in + resolve("immediate") } - var result: Int? promise.then { value in - result = value + XCTAssertEqual(value, "immediate") expectation.fulfill() } waitForExpectations(timeout: 1) - XCTAssertEqual(result, 10) - XCTAssertEqual(promise.state, .fulfilled) } - func testPromiseRejectsWithError() { - let expectation = self.expectation(description: "Promise should reject") + func testPromiseRejectsImmediately() { + enum TestError: Error { case fail } + let expectation = self.expectation(description: "Immediate reject") let promise = ServicePromise { _, reject in - reject(NSError(domain: "Test", code: 123, userInfo: nil)) + reject(TestError.fail) } - var receivedError: Error? promise.catch { error in - receivedError = error + XCTAssertTrue(error is TestError) expectation.fulfill() } waitForExpectations(timeout: 1) - XCTAssertNotNil(receivedError) - XCTAssertEqual(promise.state, .rejected) } - func testPromiseFinallyCalledOnResolve() { - let expectation = self.expectation(description: "Finally should be called") - let promise = ServicePromise { resolve, _ in - resolve(5) + func testThenAfterResolve() { + let expectation = self.expectation(description: "Then after resolve") + let promise = ServicePromise.resolve(5) + promise.then { value in + XCTAssertEqual(value, 5) + expectation.fulfill() } - var finallyCalled = false - promise.finally { - finallyCalled = true + waitForExpectations(timeout: 1) + } + + func testCatchAfterReject() { + enum TestError: Error { case fail } + let expectation = self.expectation(description: "Catch after reject") + let promise = ServicePromise.reject(TestError.fail) + promise.catch { error in + XCTAssertTrue(error is TestError) expectation.fulfill() } waitForExpectations(timeout: 1) - XCTAssertTrue(finallyCalled) } - func testPromiseFinallyCalledOnReject() { - let expectation = self.expectation(description: "Finally should be called on reject") - let promise = ServicePromise { _, reject in - reject(NSError(domain: "Test", code: 1, userInfo: nil)) + func testFinallyAfterResolve() { + let expectation = self.expectation(description: "Finally after resolve") + let promise = ServicePromise.resolve(1) + promise.finally { + expectation.fulfill() } - var finallyCalled = false + waitForExpectations(timeout: 1) + } + + func testFinallyAfterReject() { + enum TestError: Error { case fail } + let expectation = self.expectation(description: "Finally after reject") + let promise = ServicePromise.reject(TestError.fail) promise.finally { - finallyCalled = true expectation.fulfill() } waitForExpectations(timeout: 1) - XCTAssertTrue(finallyCalled) } - func testPromiseCancel() { - let expectation = self.expectation(description: "Cancel handler should be called") - let promise = ServicePromise { _, _ in } - var cancelCalled = false - promise.onCancel { - cancelCalled = true + func testAllResolves() { + let expectation = self.expectation(description: "All resolves") + let p1 = ServicePromise.resolve(1) + let p2 = ServicePromise.resolve(2) + let p3 = ServicePromise.resolve(3) + ServicePromise.all([p1, p2, p3]).then { values in + XCTAssertEqual(values.sorted(), [1, 2, 3]) expectation.fulfill() } - promise.cancel() waitForExpectations(timeout: 1) - XCTAssertTrue(cancelCalled) - XCTAssertEqual(promise.state, .cancelled) } - func testThenCalledImmediatelyIfAlreadyResolved() { - let promise = ServicePromise { resolve, _ in - resolve(99) + func testAllRejectsIfAnyFails() { + enum TestError: Error { case fail } + let expectation = self.expectation(description: "All rejects if any fails") + let p1 = ServicePromise.resolve(1) + let p2 = ServicePromise.reject(TestError.fail) + let p3 = ServicePromise.resolve(3) + ServicePromise.all([p1, p2, p3]).catch { error in + XCTAssertTrue(error is TestError) + expectation.fulfill() } - var value: Int? - let _ = promise.then { v in - value = v + waitForExpectations(timeout: 1) + } + + func testAllEmptyArray() { + let expectation = self.expectation(description: "All with empty array resolves immediately") + ServicePromise.all([]).then { values in + XCTAssertEqual(values.count, 0) + expectation.fulfill() } - XCTAssertEqual(value, 99) + waitForExpectations(timeout: 1) } - func testCatchCalledImmediatelyIfAlreadyRejected() { - let promise = ServicePromise { _, reject in - reject(NSError(domain: "Test", code: 2, userInfo: nil)) + func testThenThrowsError() { + enum TestError: Error { case fail } + let expectation = self.expectation(description: "Then throws error") + let promise = ServicePromise.resolve(1) + promise.then { _ in + throw TestError.fail + }.catch { error in + XCTAssertTrue(error is TestError) + expectation.fulfill() } - var error: Error? - let _ = promise.catch { e in - error = e + waitForExpectations(timeout: 1) + } + + func testMultipleThenHandlers() { + let expectation1 = self.expectation(description: "First then called") + let expectation2 = self.expectation(description: "Second then called") + let promise = ServicePromise.resolve("multi") + promise.then { value in + XCTAssertEqual(value, "multi") + expectation1.fulfill() } - XCTAssertNotNil(error) + promise.then { value in + XCTAssertEqual(value, "multi") + expectation2.fulfill() + } + waitForExpectations(timeout: 1) } - func testFinallyCalledImmediatelyIfAlreadyResolved() { + func testMultipleCatchHandlers() { + enum TestError: Error { case fail } + let expectation1 = self.expectation(description: "First catch called") + let expectation2 = self.expectation(description: "Second catch called") + let promise = ServicePromise.reject(TestError.fail) + promise.catch { error in + XCTAssertTrue(error is TestError) + expectation1.fulfill() + } + promise.catch { error in + XCTAssertTrue(error is TestError) + expectation2.fulfill() + } + waitForExpectations(timeout: 1) + } + func testPromiseResolvesSuccessfully() { + let expectation = self.expectation(description: "Promise should resolve") let promise = ServicePromise { resolve, _ in - resolve(42) + DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) { + resolve(42) + } } - var finallyCalled = false - let _ = promise.finally { - finallyCalled = true + promise.then { value in + XCTAssertEqual(value, 42) + expectation.fulfill() } - XCTAssertTrue(finallyCalled) + waitForExpectations(timeout: 1) } - func testFinallyCalledImmediatelyIfAlreadyRejected() { + + func testPromiseRejectsWithError() { + enum TestError: Error { case fail } + let expectation = self.expectation(description: "Promise should reject") let promise = ServicePromise { _, reject in - reject(NSError(domain: "Test", code: 3, userInfo: nil)) + DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) { + reject(TestError.fail) + } } - var finallyCalled = false - let _ = promise.finally { - finallyCalled = true + promise.catch { error in + XCTAssertTrue(error is TestError) + expectation.fulfill() } - XCTAssertTrue(finallyCalled) + waitForExpectations(timeout: 1) } - func testOnCancelCalledImmediatelyIfAlreadyCancelled() { - let promise = ServicePromise { _, _ in } - var cancelCalled = false - let _ = promise.onCancel { - cancelCalled = true + + func testThenChaining() { + let expectation = self.expectation(description: "Promise chain should resolve") + let promise = ServicePromise { resolve, _ in + resolve(10) } - promise.cancel() - XCTAssertTrue(cancelCalled) + promise + .then { value -> Int in + return value * 2 + } + .then { value -> String in + XCTAssertEqual(value, 20) + return "Final value: \(value)" + } + .then { value -> String in + XCTAssertEqual(value, "Final value: 20") + return "20" + } + .then { value -> ServicePromise in + return ServicePromise { resolve, _ in + resolve("Chained value: \(value)") + } + }.then { value in + XCTAssertEqual(value, "Chained value: 20") + expectation.fulfill() + } + waitForExpectations(timeout: 1) } - func testOnCancelNotCalledIfPromiseNotCancelled() { - let promise = ServicePromise { _, _ in } - var cancelCalled = false - let _ = promise.onCancel { - cancelCalled = true + + func testCatchAfterThen() { + enum TestError: Error { case fail } + let expectation = self.expectation(description: "Promise should catch error after then") + let promise = ServicePromise { _, reject in + reject(TestError.fail) } - XCTAssertFalse(cancelCalled) + promise + .then { value in + XCTFail("Should not be called") + } + .catch { error in + XCTAssertTrue(error is TestError) + expectation.fulfill() + } + waitForExpectations(timeout: 1) } - func testThenAfterResolve() { - let expectation = self.expectation(description: "Then should be called after resolve") - let promise = ServicePromise { resolve, _ in - resolve(20) + + func testFinallyCalledOnFulfill() { + let expectation = self.expectation(description: "Finally should be called on fulfill") + let promise = ServicePromise { resolve, _ in + resolve("done") } - var value: Int? - promise.then { v in - value = v + promise.finally { expectation.fulfill() } waitForExpectations(timeout: 1) - XCTAssertEqual(value, 20) } - func testThenAfterReject() { - let expectation = self.expectation(description: "Then should not be called after reject") - let promise = ServicePromise { _, reject in - reject(NSError(domain: "Test", code: 4, userInfo: nil)) + func testFinallyCalledOnReject() { + enum TestError: Error { case fail } + let expectation = self.expectation(description: "Finally should be called on reject") + let promise = ServicePromise { _, reject in + reject(TestError.fail) } - var value: Int? - promise.then { v in - value = v - expectation.fulfill() - }.catch { _ in - // Catch block should be called, not then + promise.finally { expectation.fulfill() } waitForExpectations(timeout: 1) - XCTAssertNil(value) - XCTAssertEqual(promise.state, .rejected) - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension ServicePromiseTests { - func testAsyncToPromise() async throws { - // let expectation = self.expectation(description: "Async promise should resolve") - var result: Int? - let promise = ServicePromise.fromAsync { () async throws -> Int in - try await Task.sleep(nanoseconds: 500_000_000) // 0.5 seconds - return 42 - } - result = try await promise.toAsync() - // await fulfillment(of: [expectation], timeout: 1) - XCTAssertEqual(result, 42) + func testResolveWithValue() { + let promise = ServicePromise.resolve(100) - print("Async promise resolved with value: \(result ?? 0)") + XCTAssertEqual(promise.value, 100) + } + func testRejectWithError() { + enum TestError: Error { case fail } + let promise = ServicePromise.reject(TestError.fail) + XCTAssertNil(promise.value) + XCTAssertTrue(!promise.isPending) } - func testPromiseToAsync() async throws { - let expectation = self.expectation(description: "Async promise should resolve") - let promise = ServicePromise { resolve, _ in - DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) { - resolve(100) - } + @available(macOS 10.15, iOS 13.0, *) + func testAsyncInitAndWait() async throws { + let promise = ServicePromise { + try await Task.sleep(nanoseconds: 100_000_000) + return 99 } - var result: Int? - promise.then { value in - result = value - expectation.fulfill() - } - await fulfillment(of: [expectation], timeout: 1) - XCTAssertEqual(result, 100) + let value = try await promise.wait() + XCTAssertEqual(value, 99) } + } From 68f6cfdb36a05731fcadd182c7934e763e2d5de6 Mon Sep 17 00:00:00 2001 From: yaochenfeng <282696845@qq.com> Date: Mon, 9 Jun 2025 20:58:12 +0800 Subject: [PATCH 2/6] feat: update Readme --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 5d195dc..c094ca9 100644 --- a/README.md +++ b/README.md @@ -54,3 +54,36 @@ struct CounterState: ServiceStateType { } } ``` + + +### 实现ServicePromise 异步使用 + +`ServicePromise` 提供了基于 Promise 的异步服务调用方式,适合需要链式调用或异步返回结果的场景。 + +#### 示例:异步获取数据 + +```swift +// 假设有一个异步获取用户信息的服务 +struct UserInfo: Codable { + let name: String + let age: Int +} + +// 定义一个 ServicePromise +let promise = ServicePromise { resolver in + // 模拟异步网络请求 + Task { + try await Task.sleep(nanoseconds: 1_000_000_000) // 1秒延迟 + let user = UserInfo(name: "张三", age: 28) + resolver.fulfill(user) + } +} + +// 使用 then 处理结果 +promise.then { user in + print("用户信息:\(user.name), 年龄:\(user.age)") +}.catch { error in + print("获取失败:\(error)") +} +``` + From 2626772db087b13fcb5070f679da4dec6052a610 Mon Sep 17 00:00:00 2001 From: FoxDock Date: Mon, 9 Jun 2025 21:16:10 +0800 Subject: [PATCH 3/6] Update ServicePromise.swift --- Sources/DFService/ServicePromise.swift | 34 ++++++++++++++------------ 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/Sources/DFService/ServicePromise.swift b/Sources/DFService/ServicePromise.swift index 5bdca14..0b5d8b5 100644 --- a/Sources/DFService/ServicePromise.swift +++ b/Sources/DFService/ServicePromise.swift @@ -139,28 +139,30 @@ extension ServicePromise { //拓展Promise 常用函数 转换错误等 public static func all(_ promises: [ServicePromise]) -> ServicePromise<[Value]> { - return ServicePromise<[Value]> { resolve, reject in - var results: [Value] = [] - var remaining = promises.count + return ServicePromise<[Value]> { resolve, reject in + var results = Array(repeating: nil, count: promises.count) + var remaining = promises.count - if remaining == 0 { - resolve([]) - return - } + if promises.isEmpty { + resolve([]) + return + } - for promise in promises { - promise.then { value in - results.append(value) - remaining -= 1 - if remaining == 0 { - resolve(results) - } - }.catch { error in - reject(error) + for (index, promise) in promises.enumerated() { + promise.then { value in + results[index] = value + remaining -= 1 + if remaining == 0 { + // 全部 fulfilled 后,转换为 [Value] + resolve(results.compactMap { $0 }) } + }.catch { error in + // 任何一个失败,立即 reject + reject(error) } } } +} public static func resolve(_ value: Value) -> ServicePromise { return ServicePromise { resolve, _ in From 3daa112d839f5e66176d803eb36b79936be3449e Mon Sep 17 00:00:00 2001 From: yaochenfeng <282696845@qq.com> Date: Mon, 9 Jun 2025 21:53:26 +0800 Subject: [PATCH 4/6] feat: update promise --- Sources/DFService/ServicePromise.swift | 33 ++++++++++---------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/Sources/DFService/ServicePromise.swift b/Sources/DFService/ServicePromise.swift index 0b5d8b5..ae0437f 100644 --- a/Sources/DFService/ServicePromise.swift +++ b/Sources/DFService/ServicePromise.swift @@ -139,30 +139,23 @@ extension ServicePromise { //拓展Promise 常用函数 转换错误等 public static func all(_ promises: [ServicePromise]) -> ServicePromise<[Value]> { - return ServicePromise<[Value]> { resolve, reject in - var results = Array(repeating: nil, count: promises.count) - var remaining = promises.count - - if promises.isEmpty { - resolve([]) - return - } - - for (index, promise) in promises.enumerated() { - promise.then { value in - results[index] = value - remaining -= 1 - if remaining == 0 { - // 全部 fulfilled 后,转换为 [Value] - resolve(results.compactMap { $0 }) + return ServicePromise<[Value]> { resolve, reject in + var results = [Value?](repeating: nil, count: promises.count) + var remaining = promises.count + + for (index, promise) in promises.enumerated() { + promise.then { value in + results[index] = value + remaining -= 1 + if remaining == 0 { + resolve(results.compactMap { $0 }) + } + }.catch { error in + reject(error) } - }.catch { error in - // 任何一个失败,立即 reject - reject(error) } } } -} public static func resolve(_ value: Value) -> ServicePromise { return ServicePromise { resolve, _ in From 1ef21eea8085e8d27d003d712f2babe34f5209e9 Mon Sep 17 00:00:00 2001 From: yaochenfeng <282696845@qq.com> Date: Mon, 9 Jun 2025 21:56:45 +0800 Subject: [PATCH 5/6] test: update promise --- .../DFServiceTests/ServicePromiseTests.swift | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Tests/DFServiceTests/ServicePromiseTests.swift b/Tests/DFServiceTests/ServicePromiseTests.swift index c106730..862ac23 100644 --- a/Tests/DFServiceTests/ServicePromiseTests.swift +++ b/Tests/DFServiceTests/ServicePromiseTests.swift @@ -263,6 +263,45 @@ final class ServicePromiseTests: XCTestCase { XCTAssertTrue(!promise.isPending) } + func testAllAndOrder() { + let expectation = self.expectation(description: "All resolves in order") + let p1 = ServicePromise.resolve(1) + let p2 = ServicePromise.resolve(2) + let p3 = ServicePromise.resolve(3) + + ServicePromise.all([p1, p2, p3]).then { values in + XCTAssertEqual(values, [1, 2, 3]) + expectation.fulfill() + } + + waitForExpectations(timeout: 1) + } + func testAllAsyncAndOrder() { + let expectation = self.expectation(description: "All resolves in order with async") + let p1 = ServicePromise { resolve, _ in + DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) { + resolve(1) + } + } + let p2 = ServicePromise { resolve, _ in + DispatchQueue.global().asyncAfter(deadline: .now() + 0.2) { + resolve(2) + } + } + let p3 = ServicePromise { resolve, _ in + DispatchQueue.global().asyncAfter(deadline: .now() + 0.3) { + resolve(3) + } + } + + ServicePromise.all([p1, p2, p3]).then { values in + XCTAssertEqual(values, [1, 2, 3]) + expectation.fulfill() + } + + waitForExpectations(timeout: 1) + } + @available(macOS 10.15, iOS 13.0, *) func testAsyncInitAndWait() async throws { let promise = ServicePromise { From d99b5b74a68e90b4ef3d2ed1d8e14e2725380ab9 Mon Sep 17 00:00:00 2001 From: yaochenfeng <282696845@qq.com> Date: Tue, 10 Jun 2025 00:02:19 +0800 Subject: [PATCH 6/6] fix: promise all --- Sources/DFService/ServicePromise.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/DFService/ServicePromise.swift b/Sources/DFService/ServicePromise.swift index ae0437f..0abf259 100644 --- a/Sources/DFService/ServicePromise.swift +++ b/Sources/DFService/ServicePromise.swift @@ -139,6 +139,9 @@ extension ServicePromise { //拓展Promise 常用函数 转换错误等 public static func all(_ promises: [ServicePromise]) -> ServicePromise<[Value]> { + guard !promises.isEmpty else { + return .resolve([]) + } return ServicePromise<[Value]> { resolve, reject in var results = [Value?](repeating: nil, count: promises.count) var remaining = promises.count