如何从用户事件中取消flatMap发布者



我有一个设置,用户可以点击一个按钮,通过flatMap启动一系列多个网络请求。我想允许用户通过点击另一个按钮来取消网络请求。对保存的可取消项调用cancel不会取消请求。我有这个班:

class ImageService : ObservableObject, UrlBuildable {
@Published var currentImage: DownloadImageEvent?
private var cancellables = Set<AnyCancellable>()

private var isCancelled: Bool = false

func downloadImage(at: URL) -> AnyPublisher<UIImage, URLError> {
return URLSession.shared.dataTaskPublisher(for: at)
.compactMap { UIImage(data: $0.data) }
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
}

func cancel() {
currentImage = nil
cancellables.removeAll()
self.isCancelled = true
print("(#function) isCancelled: (isCancelled)")
}

typealias DownloadImageEventResult = Result<DownloadImageEvent, URLError>
typealias DownloadImageEventResultPublisher = AnyPublisher<DownloadImageEventResult, Never>

func downloadAllImages() ->  DownloadImageEventResultPublisher {
let urls = buildImagesUrls().compactMap{ URL(string: $0) }
let pub: AnyPublisher<URL, Never> = urls.publisher.eraseToAnyPublisher()
var index: Int = 0
let delay: Int = 2

return pub.flatMap(maxPublishers: .max(1)) { [isCancelled] url -> DownloadImageEventResultPublisher in
print("(#function) (url)")
index += 1

// download & create event
print("(#function) isCancelled: (isCancelled)")

if (isCancelled) {
let res = Result<DownloadImageEvent, URLError>.failure(URLError(URLError.Code(rawValue: 404)))

return Just(res).eraseToAnyPublisher()
} else {
return self.downloadImage(at: url)
.print()
.map{ _ in DownloadImageEvent(
index: index,
total: urls.count,
url: url.absoluteString) }
.delay(for: RunLoop.SchedulerTimeType.Stride(TimeInterval(delay)),
scheduler: RunLoop.main)
.convertToResult()
.eraseToAnyPublisher()
}
}.eraseToAnyPublisher()
}
}

我可以看到,当cancel()被调用时,isCancelled被更新,但flatMap看到了isCancelled的旧值,因为它最初捕获了它。所以这种方法是行不通的。我曾想过使用私人主题,并让cancel()向私人主题发送事件,但我如何让flatMap取消对私人主题来源的事件的响应?

您没有在cancellables中存储任何内容,因此调用cancellables.removeAll()不会有任何效果,因为集合为空。为了取消操作,您必须在调用downloadAllImages()的位置执行此操作。呼叫站点持有令牌,只有在那里您才能取消订阅。

相关内容

  • 没有找到相关文章

最新更新