Combine转换Publisher报“Receive canceled”,token过期重试请求不生效如何解决
Combine Token自动刷新流取消问题排查与修复
核心原因
你遇到的received canceled提示,本质是Combine流中出现了未被正确捕获的类型不匹配/错误穿透,导致整个订阅链路被主动取消,错误没有走到tryCatch处理逻辑中。
排查步骤
- 先校验Publisher的错误类型是否对齐:
session.publisher(for:mapper:token:)的Failure类型必须和tryCatch返回的Publisher Failure类型完全一致。如果session返回的是自定义错误类型,没有抹成通用Error,会因为类型不匹配被Combine隐式取消流,错误不会进入tryCatch。 - 检查错误类型匹配逻辑:你当前判断
error as? ServerErrorType,如果session抛出的错误被URLSession错误、解码错误等外层包装过,就会导致类型转换失败,直接抛出未处理的错误触发取消。可以在tryCatch开头先打印error的实际类型和内容,确认匹配逻辑是否生效。 - 检查flatMap的类型兼容性:Combine默认的
flatMap要求内部Publisher的Failure类型和上游authentificator.refreshToken的Failure类型完全一致,类型不匹配就会触发流取消。 - 检查refreshToken自身的错误处理:如果强制刷新token的请求本身失败(比如refresh token也过期),这个错误没有被捕获的话也会直接穿透触发取消。
修复后的代码示例
public func executeRequest<T: Decodable, E: ServerErrorType>( _ request: HTTPRequest, mapper: ObjectMapper<T, E> ) -> AnyPublisher<(T, HTTPResponse), Error> { return authentificator .refreshToken(force: false) // 统一上游错误类型为通用Error .mapError { $0 as Error } .subscribe(on: DispatchQueue.global()) // 改用tryFlatMap允许内部抛出错误 .tryFlatMap { token -> AnyPublisher<(T, HTTPResponse), Error> in session.publisher(for: request, mapper: mapper, token: token) // 统一请求的错误类型为通用Error .mapError { $0 as Error } .tryCatch({ error -> AnyPublisher<(T, HTTPResponse), Error> in // 先打印错误调试 debugPrint("收到请求错误: \(error) 类型: \(type(of: error))") if let error = error as? E, // 直接用泛型E匹配,避免类型转换失败 error.isAuthError { return authentificator .refreshToken(force: true) .mapError { $0 as Error } .subscribe(on: DispatchQueue.global()) .flatMap { token -> AnyPublisher<(T, HTTPResponse), Error> in session.publisher(for: request, mapper: mapper, token: token) .mapError { $0 as Error } .eraseToAnyPublisher() } // 可选:添加refresh token失败的兜底处理,避免穿透 .tryCatch { refreshError -> AnyPublisher<(T, HTTPResponse), Error> in // 可以在这里触发登出逻辑 throw refreshError } .eraseToAnyPublisher() } else { throw error } }) .print() .eraseToAnyPublisher() } .receive(on: DispatchQueue.main) .print() .eraseToAnyPublisher() }
内容的提问来源于stack exchange,提问作者hbk
相关产品推荐
相关产品推荐

