繁体   English   中英

取消循环外的 DispatchWorkItem - Swift

[英]Cancel DispatchWorkItem outside loop - Swift

我一直在使用DispatchWorkItemDispatchQueue在我的应用程序中发出异步请求。 但是,我在尝试中止其中一个请求时遇到了麻烦。

我成功地使用workItem.cancel()来更改标志,然后在我想中止的地方检查它。 像这样:

for stop in self.userSettingsController.history {
    stop.PassingInfo?.removeAll()
                 
    if workItem?.isCancelled ?? false {
       print("CANCELED")
       workItem = nil
       break
    }

...

但是,在一种情况下,我没有循环可以继续检查cancelled标志是否更改,因此我无法使用上述过程中止请求。 这是代码:

  let tripQueue = DispatchQueue(label: "tripQueue")
  var tripWorkItem: DispatchWorkItem? = nil
        
  tripWorkItem = DispatchWorkItem {
            self.soapServices.GetPathsByLineAndDirection(lineCode: self.lineCode!, direction: passingInfo.Direction!) { response in
                DispatchQueue.main.async {
                    self.linePaths = response?.filter({$0.Places.contains(where: {$0.Code == self.singleStopSelected?.Code})})
                    
                    if realTime {
                        //Get estimated trip
                        self.showingRealTime = true
                        
                        if self.linePaths?.count ?? 0 > 0 {
                            self.getEstimatedTrip(lineCode: self.lineCode ?? "", direction: passingInfo.Direction ?? 0, stopCode: self.singleStopSelected?.Code ?? "", path: (self.linePaths?.first)!) { updateTripTimes in
                                //Does not work, as is expected. Just showing what I would like to achieve
                                if tripWorkItem?.isCancelled ?? false {
                                    tripWorkItem = nil
                                    return
                                }
                                
                                if updateTripTimes {
                                    DispatchQueue.main.async {
                                        self.updateTripTimes = true
                                    }
                                }
                            }
                        }
                    } else {
                        //Get trip
                        self.showingRealTime = false
                        self.getTrip(tripId: passingInfo.Id!)
                    }
                }
            }
            
            tripWorkItem = nil
        }
        
        self.currentTripWorkItem = tripWorkItem
        tripQueue.async(execute: tripWorkItem ?? DispatchWorkItem {})

有没有办法做到这一点?

提前致谢。

ps:如果这是重复的,我很抱歉,但我之前搜索过,但找不到问题。 我可能使用了错误的术语。

与其将代码放在DispatchWorkItem ,不如考虑将其包装在Operation子类中。 你得到相同的isCancelled布尔模式:

class ComputeOperation: Operation {
    override func main() {
        while ... {
            if isCancelled { break }

            // do iteration of the calculation
        }

        // all done
    }
}

为了您的网络请求,将它包装在一个自定义的AsynchronousOperation子类(如这个实现),并实现cancel ,将取消获得的网络请求。 例如:

enum NetworkOperationError: Error {
    case unknownError(Data?, URLResponse?)
}

class NetworkOperation: AsynchronousOperation {
    var task: URLSessionTask!

    init(url: URL, completion: @escaping (Result<Data, Error>) -> Void) {
        super.init()
        self.task = URLSession.shared.dataTask(with: url) { data, response, error in
            guard
                let responseData = data,
                let httpResponse = response as? HTTPURLResponse,
                200 ..< 300 ~= httpResponse.statusCode,
                error == nil
            else {
                DispatchQueue.main.async {
                    completion(.failure(error ?? NetworkOperationError.unknownError(data, response)))
                    self.finish()
                }
                return
            }

            DispatchQueue.main.async {
                completion(.success(responseData))
                self.finish()
            }
        }
    }

    override func main() {
        task.resume()
    }

    override func cancel() {
        super.cancel()
        task.cancel()
    }
}

不要迷失在上面例子的细节中。 请注意

  • 它继承了AsynchronousOperation
  • 在完成处理程序,它调用finish调用完成处理后;
  • cancel实现取消异步任务。

然后你可以定义你的队列:

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 4       // use whatever you think is reasonable

并将您的操作添加到其中:

let operation = NetworkOperation(url: url) { response in
    switch response {
    case .failure(let error):
        // do something with `error`

    case .success(let data):
        // do something with `data`
    }
}
queue.addOperation(operation)

现在,您的GetPathsByLineAndDirectiongetEstimatedTrip的问题是您没有遵循“可取消”模式,即您似乎没有返回任何可用于取消请求的内容。

所以,让我们看一个例子。 想象一下,您有一些简单的方法,例如:

func startNetworkRequest(with url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> Void) {
    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        completion(data, response, error)
    }
    task.resume()
}

您要做的是将其更改为返回可以取消的内容,即本示例中的URLSessionTask

@discardableResult
func startNetworkRequest(with url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionTask {
    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        completion(data, response, error)
    }
    task.resume()
    return task
}

现在这是一个可取消的异步任务(你可以用上面的AsynchronousOperation模式包装它)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM