Ios 快速联合收割机,如何取消执行

Ios 快速联合收割机,如何取消执行,ios,swift,xcode,combine,Ios,Swift,Xcode,Combine,我是个新手,我正试图通过解决“旧”问题来理解它 我的目标是使进程可取消,但即使我在printFizzbuzz()方法之后(或在排序延迟的情况下)调用了.cancel()方法,代码仍然保持运行(大约3秒),直到完成 我在新的Xcode项目中尝试了下面的代码,仍然是一样的 import Foundation import Combine enum PrinterError: Error { case indexError(String) case subscriptionError

我是个新手,我正试图通过解决“旧”问题来理解它

我的目标是使进程可取消,但即使我在
printFizzbuzz()
方法之后(或在排序延迟的情况下)调用了
.cancel()
方法,代码仍然保持运行(大约3秒),直到完成

我在新的Xcode项目中尝试了下面的代码,仍然是一样的

import Foundation
import Combine

enum PrinterError: Error {
    case indexError(String)
    case subscriptionError(String)
    
    var description: String {
        switch self {
        case .indexError(let descpription):
            return descpription
        case .subscriptionError(let description):
            return description
        }
    }
}

struct FizzbuzzPrinter {
    private var subscriptions = Set<AnyCancellable>()

    mutating func printFizzbuzz(fromIndex: Int, toIndex: Int, handler: @escaping (_ result: Result<Int,PrinterError>) -> Void) {
        guard toIndex > fromIndex else {
            handler(.failure(.indexError("toIndex must larger than fromIndex")))
            return
        }

        var currentIndex: Int = fromIndex
        
        Array<Int>(fromIndex ..< toIndex).publisher
            .handleEvents(receiveOutput: { index in
                currentIndex = index
            }, receiveCancel: {
                handler(.failure(.subscriptionError("cancaled at \(currentIndex)")))
            })
            .map { number -> String in
                switch (number.isMultiple(of: 3), number.isMultiple(of: 5) ) {
                case (true, true):
                    return "fizzbuzz at \(number)"
                case (true, false):
                    return "fizz at \(number)"
                case (false, true):
                    return "buzz at \(number)"
                case (false, false):
                    return String()
                }
            }
            .filter{ !$0.isEmpty }
            .sink { _ in
                handler(.success(currentIndex))
            } receiveValue: { print($0)}
            .store(in: &subscriptions)
        
    }
    
    mutating func cancelAll() {
        subscriptions.forEach{ $0.cancel()}
    }
}

var fizzBuzzPrinter = FizzbuzzPrinter()

DispatchQueue.main.async {
    fizzBuzzPrinter.printFizzbuzz(fromIndex: 1, toIndex: 60001) { result in
        switch result {
        case .failure(let printerError):
            print(printerError.description)
        case .success(let finishedIndex):
            print("finished at \(finishedIndex)")
        }
    }
}
DispatchQueue.main.async {
    fizzBuzzPrinter.cancelAll()
}

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
    fizzBuzzPrinter.cancelAll()
}
我还尝试使用.switchToLatest()运算符,但仍然无法取消它

struct FizzbuzzPrinter {
    private var subscriptions = Set<AnyCancellable>()
    
    private let publishers = PassthroughSubject<AnyPublisher<Int, Never>, Never>()
    private let finishPublisher = PassthroughSubject<Int,Never>()


    mutating func printFizzbuzz(fromIndex: Int, toIndex: Int, handler: @escaping (_ result: Result<Int,PrinterError>) -> Void) {
        guard toIndex > fromIndex else {
            handler(.failure(.indexError("toIndex must larger than fromIndex")))
            return
        }

        var currentIndex: Int = fromIndex
        
        publishers
            .switchToLatest()
            .handleEvents(receiveOutput: { index in
                currentIndex = index
            }, receiveCancel: {
                handler(.failure(.subscriptionError("cancaled at \(currentIndex)")))
            })
            .map { number -> String in
                switch (number.isMultiple(of: 3), number.isMultiple(of: 5) ) {
                case (true, true):
                    return "fizzbuzz at \(number)"
                case (true, false):
                    return "fizz at \(number)"
                case (false, true):
                    return "buzz at \(number)"
                case (false, false):
                    return String()
                }
            }
            .filter{ !$0.isEmpty }
            .sink { _ in
                handler(.success(currentIndex))
            } receiveValue: { print($0)}
            .store(in: &subscriptions)
        
        publishers.send(Array<Int>(fromIndex ..< toIndex)
                            .publisher
                            .eraseToAnyPublisher())
        
    }
    
    mutating func cancel() {
        publishers.send(finishPublisher.eraseToAnyPublisher())
        finishPublisher.send(completion: .finished)
    }
}

var fizzBuzzPrinter = FizzbuzzPrinter()

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
    fizzBuzzPrinter.cancel()
}
fizzBuzzPrinter.printFizzbuzz(fromIndex: 1, toIndex: 60001) { result in
    switch result {
    case .failure(let printerError):
        print(printerError.description)
    case .success(let finishedIndex):
        print("finished at \(finishedIndex)")
    }
}
++++++++++++++++++更新:2+++++++++++++++++++

基于@matt comment

.subscribe(on: DispatchQueue(label: "serial queue"))
和@matt Link:最后一节“施加背压”

两种方法都有效

谢谢

++++++++++++++++++更新:3+++++++++++++++++++

我修改了我以前的代码,使之成为更通用的代码,并使用

.subscribe(on: DispatchQueue.global(qos: .background))

最适合我,速度不会受到硬编码时间间隔限制,不会相互阻塞,并且可以单独取消

我调用了下面的代码(不包括一些不相关的逻辑)

+++++++++++++++++++更新4+++++++++++++++++++

刚刚做了一个项目,学习如何取消/恢复任务


问题在于您的发布者过于粗糙:它不是异步的。数组发布器只是一次发布其所有值,因此取消太晚;您正在阻塞主线程。请改用计时器发布器之类的工具,或者使用带有延迟和背压的平面图。

Hi@matt我正在尝试使printFizzbuzz()中耗时的过程可以随时取消(也可以用新任务替换),并打印出(在什么索引处)取消的过程//我读到了“取消订阅将释放以前通过附加订阅服务器分配的所有资源。”,因此我首先尝试在FizzbuzzPrinter中取消订阅以取消“fizzbuzz”过程,但它不起作用/然后尝试使用.switchToLatest()操作员提前终止进程但仍不工作问题在于,这不仅耗时,而且阻塞。@matt我想你是对的,我试过了。。订阅(在:DispatchQueue(标签:“串行队列”))“它变得可取消我想你是对的,我把它改成了基于计时器的,现在可以工作了,目前我很难理解如何使用“带有延迟和背压的平面图”,我会做更多的阅读、研究,稍后更新我的帖子//谢谢!刚刚更新了帖子,谢谢!!
.subscribe(on: DispatchQueue(label: "serial queue"))
            .flatMap(maxPublishers: .max(1)){ num in
                Just(num).delay(for: .seconds(0.01), scheduler: DispatchQueue.main)
            }

.subscribe(on: DispatchQueue.global(qos: .background))
.receive(on: DispatchQueue.main)
typealias CancelableIntTaskType = CancelableTask<AnyPublisher<Int, Never>>
var cancelableIntTasks = CancelableIntTaskType()
var taskIdArray = [UUID?]()

taskIdArray = [
    startFizzBuzzTask(50, -80, &cancelableIntTasks),
    startFizzBuzzTask(100, 200, &cancelableIntTasks),
    startFizzBuzzTask(600, 1000, &cancelableIntTasks),
    startFizzBuzzTask(2000, 2600, &cancelableIntTasks)
]

checkAllTasksStarted(taskIdArray)

DispatchQueue.main.asyncAfter(deadline: .now() + 0.0001) {
    if let id = taskIdArray.compactMap({$0}).first {
        cancelableIntTasks.cancelTaskWithID(id)
    }
}

DispatchQueue.main.asyncAfter(deadline: .now() + 0.0002) {
    cancelableIntTasks.cancelAll()
}
*id 52F started
id:52F val: 100 progress 0.0%
id:52F val: 101 progress 1.0%
id:52F val: 102 progress 2.0%
id:52F val: 103 progress 3.0%
id:52F val: 104 progress 4.0%
id:52F val: 105 progress 5.0%
id:52F val: 106 progress 6.0%
id:52F val: 107 progress 8.0%
id:52F val: 108 progress 8.0%
id:52F val: 109 progress 9.0%
id:52F val: 110 progress 10.0%
id:52F val: 111 progress 11.0%
id:52F val: 112 progress 12.0%
*id 9EA started
id:52F val: 113 progress 13.0%
id:52F val: 114 progress 15.0%
id:52F val: 115 progress 15.0%
*id 97B started
id:52F val: 116 progress 16.0%
id:52F val: 117 progress 17.0%
id:52F val: 118 progress 18.0%
id:9EA val: 600 progress 0.0%
id:9EA val: 601 progress 1.0%
id:97B val: 2000 progress 0.0%
id:9EA val: 602 progress 1.0%
id:9EA val: 603 progress 1.0%
id:9EA val: 604 progress 1.0%
id:97B val: 2001 progress 1.0%
id:9EA val: 605 progress 2.0%
id:97B val: 2002 progress 1.0%
id:52F val: 119 progress 19.0%
id:9EA val: 606 progress 2.0%
4 tasks initiated 3 started
task at index 0 failed
id:97B val: 2003 progress 1.0%
id:52F val: 120 progress 20.0%
id:9EA val: 607 progress 2.0%
id:97B val: 2004 progress 1.0%
id:9EA val: 608 progress 2.0%
id:52F val: 121 progress 21.0%
id:97B val: 2005 progress 1.0%
id:9EA val: 609 progress 3.0%
id:97B val: 2006 progress 1.0%
id:9EA val: 610 progress 3.0%
id:97B val: 2007 progress 2.0%
id:9EA val: 611 progress 3.0%
id:97B val: 2008 progress 2.0%
id:9EA val: 612 progress 3.0%
id:97B val: 2009 progress 2.0%
id:9EA val: 613 progress 4.0%
id:97B val: 2010 progress 2.0%
id:9EA val: 614 progress 4.0%
id:97B val: 2011 progress 2.0%
id:52F val: 122 progress 22.0%
id:9EA val: 615 progress 4.0%
id:52F val: 123 progress 23.0%
id:9EA val: 616 progress 4.0%
id:52F val: 124 progress 24.0%
id:9EA val: 617 progress 5.0%
id:52F val: 125 progress 25.0%
id:97B val: 2012 progress 2.0%
id:9EA val: 618 progress 5.0%
**52F canceled at 125 progress: 25.0%
**97B canceled at 2012 progress: 2.0%
**9EA canceled at 619 progress: 5.0%
id:9EA val: 619 progress 5.0%
id:52F val: 126 progress 26.0%