【问题标题】:Synchronous Alamofire Request with DispatchGroup使用 DispatchGroup 的同步 Alamofire 请求
【发布时间】:2018-05-22 22:19:19
【问题描述】:

我需要等到 Alamofire 请求完成获取数据。 (错误或值)。我在另一个函数的 for 循环中调用 Alamofire 函数,以便在调用第二个 for 循环之前完成 Alamofire 请求。例如;第一个循环 -> 第一个请求 -> 第二个循环 -> 第二个请求......依此类推。现在它进入第一个循环 -> 第二个循环 -> 并且在所有循环完成后请求响应正在转向。

请求函数:

 func like(sender_id: String, completion: @escaping (String?) -> ()){
    let group = DispatchGroup()
    if let key = api_key{
        let headers: HTTPHeaders = [
            "X-Auth-Token": key,
            "Accept": "application/json"
        ]
        group.enter()
        Alamofire.request(baseUrl + likeURL + sender_id, method: .get, parameters: nil, encoding: URLEncoding.default, headers: headers).validate()
            .responseJSON { (response) -> Void in
                guard response.result.isSuccess else {
                    print("Error while doing : \(response.result.error)")
                    completion(nil)
                    group.leave()
                    return
                }
                if let error = response.error{
                    completion(nil)
                    group.leave()
                    print("Error Occured with Request: \(error)")
                }
                if let jsonData = response.data{
                    let json = JSON(data: jsonData)
                    print(json)
                    group.leave()
                    if let matched = json["match"].bool{
                        completion(matched.description)
                        print("Liked!: \(matched)")
                        if(matched){
                        }
                    }else{
                        group.leave()
                        "There is an error with JSON"
                    }
                }}
    }
}

我在哪里打电话:

 func like_all(completion: @escaping() -> ()){
    for user in SharedUsers.sharedUser.users{
        if let id = user.id{
            Network.sharedInstance.like(sender_id: id) { result in
                print(result)
            }
        }
        else{
            continue
        }
    }
    completion()
}

【问题讨论】:

  • 无关,在您的完成处理程序中,您有一个执行路径,您可以在其中两次调用leave。你应该解决这个问题。或者,更好的是,如果您继续使用这种调度组模式,我建议在关闭开始时使用defer { group.leave() },然后拉出所有其他leave 调用,避免任何执行路径的风险你没有打电话给leave。坦率地说,这没有实际意义,因为您根本不应该使用调度组,而只是仅供参考。
  • 我查看了 promisekit,但我不确定它是否适合我的问题。
  • 下面我概述了如何做到这一点的两种替代方法,但 PromiseKit 绝对是解决这个问题的另一种方法。关键思想是,在启动另一个异步进程之前,您永远不想wait,而是使用其中一种模式在前一个请求完成时异步触发一个请求。这样可以避免阻塞线程(并消除任何死锁风险)。

标签: ios swift loops alamofire dispatch


【解决方案1】:

我通过在 Alamofire 每次返回值时调用 index + 1 的类似函数来解决它。我还在获取请求的完成部分调用函数。

代码如下:

    @objc func action(_ sender: LGButton) {
        sender.titleString = "Started to Like :)"
        Network.sharedInstance.get_rec(completion: { (result) in
            if(result != nil)
            {
                Network.sharedInstance.like(sender: 0, completion: { (result) in
                    //print(result)
                })
            }
        })
}

点赞功能:

 func like(sender: Int, completion: @escaping (String?) -> ()){
    if let key = api_key{
        let headers: HTTPHeaders = [
            "X-Auth-Token": key,
            "Accept": "application/json"
        ]
        print(sender)
        print(SharedUsers.sharedUser.users.count)
        if(sender < SharedUsers.sharedUser.users.count){
            if let user_id = SharedUsers.sharedUser.users[sender].id{
                Alamofire.request(baseUrl + likeURL + user_id, method: .get, parameters: nil, encoding: URLEncoding.default, headers: headers).validate()
                    .responseJSON { (response) -> Void in
                        guard response.result.isSuccess else {
                            print("Error while doing : \(response.result.error)")
                            completion(nil)
                            return
                        }
                        if let error = response.error{
                            completion(nil)
                            print("Error Occured with Request: \(error)")
                        }
                        if let jsonData = response.data{
                            let json = JSON(data: jsonData)
                            if let matched = json["match"].bool{
                                completion(matched.description)
                                print("Liked!: \(matched)")
                                if(sender <= SharedUsers.sharedUser.users.count){
                                    self.like(sender: sender + 1, completion: {result in
                                        //print(result)
                                    })
                                }else{
                                    return
                                }
                                if(matched){
                                }
                            }else{
                                "There is an error with JSON"
                            }}}
            }else{return}
        }}}

【讨论】:

    【解决方案2】:

    您正在使用调度组,显然是为了在函数结束时等待组。但是你没有等待它,所以你没有得到你正在寻找的同步行为。

    但这很好,因为如果您在主线程上为该组(或信号量,实现此行为的其他模式)wait,您不仅会阻塞主线程(这会导致可怕的用户体验和风险让您的应用程序被看门狗进程杀死),您将陷入死锁,因为responseJSON 使用主队列作为其完成处理程序。因此,在您向调度组/信号量添加wait() 调用之前,请确保将整个for 循环异步调度到某个后台线程。这样可以避免阻塞主线程并消除死锁风险。

    但是这整个模式从根本上来说是有缺陷的,因为你真的不应该使用调度组或信号量来使其同步。这引发了几个问题:

    第一个问题是为什么要使这个同步。网络请求具有固有的延迟,因此执行一系列请求会非常慢。仅在绝对必要时才执行此请求序列(例如,无法形成每个请求,因为它需要先前请求的响应中的某些内容)。但这里的情况似乎并非如此。那么为什么不需要这个过程呢。

    但是让我们假设您绝对必须按顺序执行这些操作(据我所知,这里不正确,但让我们梳理一下)。那么有两种模式可以连续而不是同时执行一系列请求:

    • 您可以完全丢失这个for 循环,而只需有一个发送第n 个请求并在其完成处理程序中发送请求n+1 的例程。这完全消除了调度组/信号量阻塞线程的需要。

    • 或者您可以将其包装在操作中(例如https://stackoverflow.com/a/27022598/1271826)并使用操作队列。

    【讨论】:

    • 我想这样做,因为我有一个用户类,它由我的第一个请求填充,然后我开始使用来自用户类的信息进行第二个请求。 like_all 函数返回后,它再次执行第一个请求,然后再次执行 like_all,依此类推....
    • 你能给我一个例子吗:“你可以完全失去这个 for 循环,只需要一个发送第 n 个请求并在完成时发送请求 n+1 的例程处理程序。这完全消除了调度组/信号量阻塞线程的需要。"?
    猜你喜欢
    • 1970-01-01
    • 2016-08-19
    • 2017-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-18
    • 2015-07-26
    • 1970-01-01
    相关资源
    最近更新 更多