当 retry
收到 .failure
结束事件时,它重试的方式是给它所链接的操作符或发布者重新创建订阅。
当尝试请求连接不稳定的网络资源时,通常需要 retry 操作符,或者再次请求时可能会成功的情况。
如果指定的重试次数全部失败,则将 .failure
结束事件传递给订阅者。
在下面的示例中,我们将 retry 与 delay 操作符相结合使用。 我们使用延迟操作符在下一个请求之前使其出现少量随机延迟。 这使得重试的尝试行为被分隔开,使重试不会快速连续的发生。
此示例还包括使用 tryMap 操作符以更全面地检查从 dataTaskPublisher
返回的任何 URL 响应。
服务器的任何响应都由 URLSession
封装,并作为有效的响应转发。
URLSession
不将 404 Not Found 的 http 响应视为错误响应,也不将任何 50x 错误代码视作错误。
使用 tryMap
,我们可检查已发送的响应代码,并验证它是 200 的成功响应代码。
在此示例中,如果响应代码不是 200 ,则会抛出一个异常 —— 这反过来又会导致 tryMap 操作符传递 .failure
事件,而不是数据。
此示例将 tryMap
设置在 retry 操作符 之后,以便仅在网站未响应时重新尝试请求。
let remoteDataPublisher = urlSession.dataTaskPublisher(for: self.URL!)
.delay(for: DispatchQueue.SchedulerTimeType.Stride(integerLiteral: Int.random(in: 1..<5)), scheduler: backgroundQueue) (1)
.retry(3) (2)
.tryMap { data, response -> Data in (3)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw TestFailureCondition.invalidServerResponse
}
return data
}
.decode(type: PostmanEchoTimeStampCheckResponse.self, decoder: JSONDecoder())
.subscribe(on: backgroundQueue)
.eraseToAnyPublisher()
-
delay 操作符将流经过管道的结果保持一小段时间,在这个例子中随机选择1至5秒。通过在管道中添加延迟,即使原始请求成功,重试也始终会发生。
-
重试被指定为尝试3次。 如果每次尝试都失败,这将导致总共 4 次尝试 - 原始请求和 3 次额外尝试。
-
tryMap 被用于检查 dataTaskPublisher 返回的数据,如果服务器的响应数据有效,但不是 200 HTTP 响应码,则返回
.failure
完成事件。
Warning
|
使用 retry 操作符与 URLSession.dataTaskPublisher 时,请验证你请求的 URL 如果反复请求或重试,不会产生副作用。 理想情况下,此类请求应具有幂等性。 如果没有,retry 操作符可能会发出多个请求,并产生非常意想不到的副作用。 |