我使用 GCD 的
DispatchWorkItem
来跟踪发送到 firebase 的数据。
我做的第一件事是声明 2 个类型为
DispatchWorkItem
的类属性,然后当我准备好将数据发送到 firebase 时,我用值初始化它们。
第一个属性名为
errorTask
。初始化时,将 cancels
设置为 firebaseTask
,然后打印“errorTask已触发”。它有一个 nil
,如果 errorTask 在此之前没有取消,它将在 0.0000000001 秒内调用它。第二个属性名为
DispatchAsync Timer
。初始化时,它包含一个将数据发送到 firebase 的函数。如果 firebase 回调成功,则取消
firebaseTask
并设置为 errorTask
,然后打印一条打印语句“firebase 回调已到达”。我还检查 firebaseTask 是否被取消。问题是
nil
内的代码总是在到达
errorTask
回调之前运行。 firebaseTask
代码取消了errorTask
并将其设置为nil,但由于某种原因firebaseTask
仍然运行。我不明白为什么? 打印语句支持 errorTask 首先运行的事实,因为
firebaseTask
总是在
"errorTask fired"
之前打印。即使 errorTask 使这些事情发生,为什么 firebaseTask 没有被取消并设置为 nil?
在我的实际应用程序中,如果用户向 Firebase 发送一些数据,则会出现活动指示器。一旦达到 firebase 回调,活动指示器就会消失,并向用户显示一条警报,表明操作已成功。但是,如果活动指示器上没有计时器并且回调从未到达,那么它将永远旋转。
"firebase callback was reached"
有一个设置为 15 秒的计时器,如果回调未到达,则会显示错误标签。十分之九的情况总是有效的。
发送数据至FB
需要的时间超过15秒
DispatchAsyc after
firebaseTask
代码块关闭actiInd,显示errorLabel,并取消
errorTask
并将其设置为nil。 一旦 firebaseTask 被取消并设置为 nil,我认为它内部的所有内容都会停止,因为回调从未到达。这可能是我困惑的原因。 似乎即使
firebaseTask
被取消并设置为 nil,firebaseTask
不知何故仍在运行,我也需要取消它。我的代码:
someRef?.updateChildValues(...
var errorTask:DispatchWorkItem?
var firebaseTask:DispatchWorkItem?
@IBAction func buttonPush(_ sender: UIButton) {
// 1. initialize the errorTask to cancel the firebaseTask and set it to nil
errorTask = DispatchWorkItem{ [weak self] in
self?.firebaseTask?.cancel()
self?.firebaseTask = nil
print("errorTask fired")
// present alert that there is a problem
}
// 2. if the errorTask isn't cancelled in 0.0000000001 seconds then run the code inside of it
DispatchQueue.main.asyncAfter(deadline: .now() + 0.0000000001, execute: self.errorTask!)
// 3. initialize the firebaseTask with the function to send the data to firebase
firebaseTask = DispatchWorkItem{ [weak self] in
// 4. Check to see the if firebaseTask was cancelled and if it wasn't then run the code
if self?.firebaseTask?.isCancelled != true{
self?.sendDataToFirebase()
}
// I also tried it WITHOUT using "if firebaseTask?.isCancelled... but the same thing happens
}
// 5. immediately perform the firebaseTask
firebaseTask?.perform()
}
func sendDataToFirebase(){
let someRef = Database.database().reference().child("someRef")
someRef?.updateChildValues(myDict(), withCompletionBlock: {
(error, ref) in
// 6. if the callback to firebase is successful then cancel the errorTask and set it to nil
self.errorTask?.cancel()
self.errorTask? = nil
print("firebase callback was reached")
})
}
时,它不会执行抢先取消。它当然与
DispatchWorkItem
通话无关。它所做的只是执行 updateChildValues
属性的线程安全设置,如果您手动迭代循环,则可以定期检查并在看到任务被取消时提前退出。因此,在任务开始时检查 isCancelled
并不是非常有用的模式:如果您取消尚未开始的工作项,它就永远不会开始,并且您将永远不会达到此
isCancelled
测试。如果任务已经开始,它很可能在调用 isCancelled
之前就通过了 isCancelled
测试。底线,尝试对 cancel
请求进行计时,以便在任务开始后但在进行
cancel
测试之前准确接收这些请求,其效用有限。通常,如果您有想要取消的异步任务,您可以将其包装在异步自定义 isCancelled
子类中,并实现一个
Operation
方法来停止底层工作。操作队列只是提供比调度队列更优雅的模式来取消异步任务。但所有这些都假设底层异步任务提供了一种取消它的机制,而且我不知道 Firebase 是否提供了一种有意义的机制来做到这一点。我当然没有在他们的任何例子中看到这一点。所以所有这些可能都毫无意义。更好的是,现在我们可能会使用 Swift 并发,它提供一流的取消支持。它仍然是一种协作取消模式,但许多 cancel
-
async
API 提供本机取消支持。尤其是对于结构化并发,它提供了从父任务到子任务的优雅取消传播。我可能建议您远离问题中的特定代码模式,并描述您想要完成的任务。我们不要纠缠于您对更广泛问题的特定尝试解决方案,而是让我们了解更广泛的目标是什么,然后我们可以讨论如何解决该问题。