【问题标题】:Background thread - two network calls [duplicate]后台线程 - 两个网络调用[重复]
【发布时间】:2021-07-04 23:22:03
【问题描述】:

我在 completeOnboarding 方法中有两个方法,它们都有网络操作,应该在后台线程中完成,如下所示。但是,我想知道我是否在做为什么首先调用completion(true),我该如何处理这个问题?

DispatchQueue.global(qos: .background).async {
    self?.completeOnboarding( completion: { (success) in
      DispatchQueue.main.async {
        if success {
         print("success")
        } else {
         print("failed")
        }
     }
 })

func completeOnboarding(completion: @escaping(Bool) -> Void){

   // has network post operation
    classRegistration() {(success) in
      if !success {
        completion(false)
        return
      }
    }

    // has network post operation
    classLocation() { (success) in
      if !success {
        completion(false)
        return
      }
    }
  completion(true)
}

【问题讨论】:

  • 想必classRegistrationclassLocation是异步方法,所以马上就返回了
  • 是的,这些是异步方法。这两种方法都在对服务进行网络调用。
  • 与您的stackoverflow.com/questions/67030741/…几乎完全相同

标签: ios swift multithreading


【解决方案1】:

最终完成(true) 不等待 classLocation() 和 classRegistration() 调用。如果您有多个网络调用并且您想等待所有调用完成,您可以(一种方法)将它们添加到 DispatchGroup 并等待该调用完成:

func dispatchAndWait(completion: @escaping () -> Void) {
    func networkOne(completion: @escaping (_ success: Bool) -> Void) {
        print("[DEBUG] Enter \(#function)")

        DispatchQueue.main.asyncAfter(deadline: .now() + Double.random(in: 0...3)) {
            completion(Bool.random())
        }
        print("[DEBUG] Return \(#function)")
        
    }
    
    func networkTwo(completion: @escaping (_ success: Bool) -> Void) {
        print("[DEBUG] Enter \(#function)")
        
        DispatchQueue.main.asyncAfter(deadline: .now() + Double.random(in: 0...3)) {
            completion(Bool.random())
        }
        print("[DEBUG] Return \(#function)")
    }
    
    // Create a DispatchGroup and add both calls
    let dispatchGroup = DispatchGroup()
    // Enter first network call
    dispatchGroup.enter()
    networkOne { success in
        print("[DEBUG] Complete networkOne with success: \(success)")
        // Exit first network call
        dispatchGroup.leave()
    }
    
    // Enter second network call
    dispatchGroup.enter()
    networkTwo { success in
        print("[DEBUG] Complete networkTwo with success: \(success)")
        // Exit second network call
        dispatchGroup.leave()
    }
    
    // notify gets called when all tasks have exited
    dispatchGroup.notify(queue: DispatchQueue.main) {
        completion()
    }
}

还有一点建议:

classRegistration() {(success) in
  if !success {
    completion(false)
    return
  }
}

在success==true的情况下永远不会完成,你应该确保在每条路径上都调用完成

classRegistration() {(success) in
  // ... do whatever needs to be done here
  completion(success)
}

【讨论】:

    【解决方案2】:
    1. 假设 classRegistration 需要成功才能开始 classLocation --

    又快又脏 --

    func completeOnboarding(completion: @escaping(Bool) -> Void){
    
       // has network post operation
        classRegistration() {(success) in
          if success {
            
            // has network post operation
            classLocation() { (success) in
                completion(success)
            }
    
          } else {
            completion(false)
          }
        }
    }
    

    正确的方式(其他包括 - NSOperations with dependency, Dispatch group)

    func completeOnboarding(completion: @escaping(Bool) -> Void){
       
       let serialQueue = DispatchQueue(label: "classname.serial")
    
       var proceedWithSuccess = true
    
       serialQueue.async {
           
           serialQueue.suspend() //run 1 operation at a time
       
           classRegistration() {(success) in
                proceedWithSuccess = success
                serialQueue.resume() //let next operation run
           }
       }
       
       serialQueue.async {
        
        guard proceedWithSuccess else { return } 
    
          serialQueue.suspend()
    
              classLocation() { (success) in
    
               proceedWithSuccess = success
               serialQueue.resume()
           }
       }
              
       serialQueue.async {
          completion(proceedWithSuccess)
       }
    }
    
    1. 如果您希望 classLocation() 即使注册失败也能触发 - 只需去掉上面的保护语句即可。

    如果由我决定,我会为异步操作使用自定义 NSOperation 子类并明确提及操作之间的依赖关系,但它需要大量样板代码(也许稍后需要研究);不过,在这种情况下,串行队列(或来自其他答案的调度组)对您来说应该足够了。

    【讨论】:

    • 如果串行队列不起作用,您可能必须在函数外部声明它 - 它应该按原样工作。
    猜你喜欢
    • 2017-06-03
    • 2011-10-15
    • 1970-01-01
    • 1970-01-01
    • 2021-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多