【问题标题】:Swift wait for async taskSwift 等待异步任务
【发布时间】:2017-07-22 22:58:59
【问题描述】:

在后台线程中我们有:

defer {
    cleanup()
}

loadData()

if error {
    return
}

processData()

DispatchQueue.main.asyncAfter(deadline: delay) {  //Delay = now + 0-2 seconds
     updateUI()
}

问题是我们要确保延迟cleanUp() 代码在updateUI() 之后运行。就目前而言,这不会发生,因为updateUI() 运行异步。

我的想法是在延迟期间休眠/阻塞,而不是异步运行。这将在 updateUI() 完成后运行延迟 cleanUp()

你怎么能做到这一点?还是有更好的办法?

【问题讨论】:

  • 你可以Thread.sleep(),但是这有很严重的代码味道。我们通常希望避免在任何时间段内(或者至少超过几毫秒)阻塞线程(甚至是后台线程)。不用说,永远不要在主线程上sleep
  • 我不太明白你在说什么:你想做一些功能上等同于:sleep(delay); DispatchQueue.main.async { doStuff() }?
  • @joeybladb 我更新了描述以提供更多上下文。
  • 我猜processData 是异步的?如果是这样,给它一个完成处理程序闭包参数并从那里调用updateUI。但不要sleep 或使用asyncAfter
  • 如果问题只是 updateUI() 是异步的,它应该有一个完成块,您可以在其中调用 cleanup()

标签: ios swift asynchronous


【解决方案1】:

您可以使用信号量告诉清理任务等到updateUI 完成:

let semaphore = DispatchSemaphore(value: 1)
defer {
    semaphore.wait()
    cleanup()
}

loadData()

if error {
    // If we exit here, the semaphore would have never been used
    // and cleanup will run immediately
    return
}

processData()
semaphore.wait() // here, we claim the semaphore, making cleanup
                 // wait until updateUI is done
DispatchQueue.main.asyncAfter(deadline: delay) {
     updateUI()
     semaphore.signal()
}

【讨论】:

  • 谢谢-使用 DispatchGroup(进入离开等待)另一种实现方式?
  • 是的,你也可以使用DispatchGroup来实现它。我现在正在进行一个大量使用信号量的项目,所以我有点偏见:)
  • 谢谢。我喜欢这些选项,但意识到我可以重构代码来解决这个问题。
【解决方案2】:

意识到我可以改变代码的结构:

loadData()

if error {
    log.error("Error")
} else {
   processData()
}

DispatchQueue.main.asyncAfter(deadline: delay) {  //Delay = now + 0-2 seconds
  updateUI()
  cleanup()
}

【讨论】:

  • 硬编码延迟是一种非常糟糕的编程习惯,并不是 100% 可靠的。为什么不在异步任务的完成处理程序中更新 UI?
  • 做到了。谢谢。
  • @MarcusLeon 在完成处理程序中更新 UI 时不需要延迟,除非您希望用户再等待几秒钟。
  • 是的,这实际上是目标.. processData() 可以很快完成.. 我们显示一个刷新指示器,当方法快速完成时它会闪烁。所以我们设置了 1.25 秒的最小时间,这就是 delay 的含义。
  • @MarcusLeon 我明白了。对于那些工作正常的目的。
【解决方案3】:

另一种选择是使用DispatchGroup()

func doWork() {

   let group = DispatchGroup()

   group.enter() //Enter #1
   loadData { result in

      switch (result) {
      case .success(_):
           group.enter()//Enter #2
           processData { group.leave()//Leave #2 }
      case .failure(let error):
           //Do something nice with the error
           print(error)
      }

      group.leave()//Leave #1
    }

    //All the code inside this block will be executed on the mainThread when all tasks will be finished.
     group.notify(queue: .main) { [weak self] in

         guard let strongSelf = self else { return }

         strongSelf.updateUI()
         strongSelf.cleanup()
     }
}

private func updateUI() {
    //All your stuff
}

private func cleanup() {
    //All your stuff
}

private func loadData(completion: (Result<(), Error>) -> ()) {
    //All your stuff
    if error {
       completion(.failure(error))
    }
    else {
       completion(.success(()))
    }
}

private func processData(completion: () -> ()) {
    //All your stuff
    completion()
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-18
    • 2014-09-06
    • 1970-01-01
    • 2013-02-10
    • 1970-01-01
    • 1970-01-01
    • 2019-04-08
    • 2014-10-01
    相关资源
    最近更新 更多