Swift:依次处理多个异步请求.如何等待上一个请求完成? [英] Swift: Multiple async requests in order. How to wait for previous request to finish?

查看:105
本文介绍了Swift:依次处理多个异步请求.如何等待上一个请求完成?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序中,作为身份验证过程的一部分,用户可以使用其Facebook帐户登录-我正在使用Facebook iOS SDK来处理此过程.身份验证完成后,我向Facebook图形api发出请求以获取用户个人资料数据(这是第一个异步请求).第二个异步请求也发送到Facebook graph api,以请求安装了该应用程序的用户朋友列表.

As part of the authentication process in my app, users can sign in using their Facebook account - I'm using the Facebook iOS SDK to handle this process. Once authentication is complete, I make a request to Facebook graph api to fetch the users profile data (This is the first async req). The second async request is also to Facebook graph api to request the users friends list who have the app installed.

此函数中的最后一个请求和第三个请求向我开发的API发送异步POST请求,以发布从Facebook收集的所有数据.最后,一旦完成,便允许用户使用该应用程序.但是事实并非如此,Facebook请求似乎在对API的POST请求之前没有完成,因此推高了空白数据.我不介意对Facebook的前两个请求以什么顺序完成,但是在允许用户进入应用程序之前,我需要将数据成功发布到API.我已经尝试过使用信号量和调度组,但是在查看控制台时,事情并没有以正确的顺序运行,而且我从API数据库中可以看到正在插入空值.

The final and third request in this function makes a async POST request to an API I've developed to post all of the data collected from Facebook. Finally once this is complete the user is allowed in to the app. However this is not the case, it seems that the Facebook requests do not complete before the POST request to the API and it is therefor pushing up blank data. I don't mind in what order the first 2 requests to Facebook finish, however I NEED the data to be successfully posted to the API before allowing the user in to the app. I've tried using semaphores and Dispatch groups, however when looking at the console, things are not running the in the correct order and I can see from the API database that null values are being inserted.

身份验证控制器

 // Successful login, fetch faceook profile
            let group = DispatchGroup()
            group.enter()
            // Redirect to tab bar controller should not happen until fetchProfile() has finished 
            // Redirect should not happen if fetchProfile() errors
            self.fetchProfile() 
            group.leave()

            // Redirect to tab bar controller
            let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
            let tabBarController = storyboard.instantiateViewController(withIdentifier: "tabBarController") as! UITabBarController
            self.present(tabBarController, animated: true, completion: nil)

更新的Facebook提取个人资料

 // Facebook Profile Request
    func fetchProfile() {

    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "User")
    let user = appDelegate.user
    var facebookFriends = [String?]()

    do {
        let results = try managedContext?.fetch(fetchRequest)
        fetchedUser = results![0] as? NSManagedObject
    }
    catch {
        print("Error fetching User entity")
        return
    }


    let group = DispatchGroup()
    print("Starting Step 1")
    group.enter()

    // Facebook Profile
    let parameters = ["fields": "id, email, first_name, last_name, picture.width(500).height(500), birthday, gender"]
    FBSDKGraphRequest(graphPath: "me", parameters: parameters).start { (connection, result, error) -> Void in

        if error != nil {
            print(error)
            return
        }

        let result = result as? NSDictionary

        if let providerID = result?["id"] as? String {
            user.provider_id = providerID
            self.fetchedUser!.setValue(providerID, forKey: "provider_id")
        }

        if let firstName = result?["first_name"] as? String {
            user.first_name = firstName
            self.fetchedUser!.setValue(firstName, forKey: "first_name")
        }

        if let lastName = result?["last_name"] as? String {
            user.last_name = lastName
            self.fetchedUser!.setValue(lastName, forKey: "last_name")
        }

        if let email = result?["email"] as? String {
            user.email = email
            self.fetchedUser!.setValue(email, forKey: "email")
        }

        if let picture = result?["picture"] as? NSDictionary, let data = picture["data"] as? NSDictionary, let url = data["url"] as? String {
            user.avatar = url
            self.fetchedUser!.setValue(url, forKey: "avatar")
        }

        if let birthday = result?["birthday"] as? String {
            user.birthday = birthday
            self.fetchedUser!.setValue(sqlDate, forKey: "birthday")
        }

        if var gender = result?["gender"] as? String {
            user.gender = gender
            self.fetchedUser!.setValue(gender, forKey: "gender")
        }

        group.leave()
        print("Step 1 Done")

        group.enter()
        print("Starting Step 2")

        // Facebook Friends Request
        FBSDKGraphRequest(graphPath: "me/friends", parameters: ["fields": "id, first_name, last_name, picture"]).start { (connection, result, error) -> Void in

            if error != nil {
                print(error)
                return
            }

            let result = result as! [String:AnyObject]

            for friend in result["data"] as! [[String:AnyObject]] {
                let id = friend["id"] as! String
                facebookFriends.append(id)
            }

            group.leave()
            print("Step 2 Done")

            // User POST Request
            var dictionary = self.fetchedUser?.dictionaryWithValues(forKeys: ["provider", "provider_id", "first_name", "last_name", "email", "avatar", "birthday", "gender"])

            if facebookFriends.count > 0 {
                dictionary?["friends"] = facebookFriends
            }

            let data = NSMutableDictionary()
            data.setValuesForKeys(dictionary!)

            //let semaphore = DispatchSemaphore(value: 2)
            group.enter()
            print("Starting Step 3")

            do {
                // Here "jsonData" is the dictionary encoded in JSON data
                let jsonData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)

                // Here "decoded" is of type `Any`, decoded from JSON data
                let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])

                // Final dict
                if let dictFromJSON = decoded as? [String:String] {

                    let endpoint = "http://endpoint.com/user"
                    let url = URL(string: endpoint)
                    let session = URLSession.shared
                    var request = URLRequest(url: url!)

                    request.httpMethod = "POST"
                    request.httpBody = try JSONSerialization.data(withJSONObject: dictFromJSON, options: [])
                    request.addValue("application/json", forHTTPHeaderField: "Accept")
                    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
                    session.dataTask(with: request, completionHandler: { (data, response, error) -> Void in

                        if error != nil {
                            //semaphore.signal()
                            group.leave()
                            print(error)
                            return
                        }

                        do {
                            // Save response
                            let json = try(JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String: AnyObject])

                            if let userID = json?["user_id"] {
                                user.user_id = userID as? Int
                                self.fetchedUser!.setValue(userID, forKey: "user_id")
                            }

                            if let friends = json?["friends"] , !(friends is NSNull){
                                user.friends = friends as? [String]
                                self.fetchedUser!.setValue(friends, forKey: "friends")
                            }

                            group.leave()
                            //semaphore.signal()

                        } catch let jsonError {
                            print(jsonError)
                            return
                        }

                    }).resume()

                }
            } catch {
                print(error.localizedDescription)
            }

            // Wait to async task to finish before moving on
            //_ = semaphore.wait(timeout: DispatchTime.distantFuture)
            print("Step 3 Done")
        }
    }
}

推荐答案

在闭包自身内部的每个闭包之后移动代码,以使其在运行之前一直等到之前的代码:

Move the code after each closure inside the closure itself so that it waits until the code before it before running:

// Facebook Profile Request
func fetchProfile() {

    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "User")
    let user = appDelegate.user
    var facebookFriends = [String?]()

    do {
        let results = try managedContext?.fetch(fetchRequest)
        fetchedUser = results![0] as? NSManagedObject
    }
    catch {
        print("Error fetching User entity")
        return
    }


    let group = DispatchGroup()
    print("Starting Step 1")
    group.enter()

    // Facebook Profile
    let parameters = ["fields": "id, email, first_name, last_name, picture.width(500).height(500), birthday, gender"]
    FBSDKGraphRequest(graphPath: "me", parameters: parameters).start { (connection, result, error) -> Void in

        if error != nil {
            print(error)
            return
        }

        let result = result as? NSDictionary

        if let providerID = result?["id"] as? String {
            user.provider_id = providerID
            self.fetchedUser!.setValue(providerID, forKey: "provider_id")
        }

        if let firstName = result?["first_name"] as? String {
            user.first_name = firstName
            self.fetchedUser!.setValue(firstName, forKey: "first_name")
        }

        if let lastName = result?["last_name"] as? String {
            user.last_name = lastName
            self.fetchedUser!.setValue(lastName, forKey: "last_name")
        }

        if let email = result?["email"] as? String {
            user.email = email
            self.fetchedUser!.setValue(email, forKey: "email")
        }

        if let picture = result?["picture"] as? NSDictionary, let data = picture["data"] as? NSDictionary, let url = data["url"] as? String {
            user.avatar = url
            self.fetchedUser!.setValue(url, forKey: "avatar")
        }

        if let birthday = result?["birthday"] as? String {
            user.birthday = birthday
            self.fetchedUser!.setValue(sqlDate, forKey: "birthday")
        }

        if var gender = result?["gender"] as? String {
            user.gender = gender
            self.fetchedUser!.setValue(gender, forKey: "gender")
        }

        group.leave()
        print("Step 1 Done")

        group.enter()
        print("Starting Step 2")

        // Facebook Friends Request
        FBSDKGraphRequest(graphPath: "me/friends", parameters: ["fields": "id, first_name, last_name, picture"]).start { (connection, result, error) -> Void in

            if error != nil {
                print(error)
                return
            }

            let result = result as! [String:AnyObject]

            for friend in result["data"] as! [[String:AnyObject]] {
                let id = friend["id"] as! String
                facebookFriends.append(id)
            }

            group.leave()
            print("Step 2 Done")

            // User POST Request
            var dictionary = self.fetchedUser?.dictionaryWithValues(forKeys: ["provider", "provider_id", "first_name", "last_name", "email", "avatar", "birthday", "gender"])

            if facebookFriends.count > 0 {
                dictionary?["friends"] = facebookFriends
            }

            let data = NSMutableDictionary()
            data.setValuesForKeys(dictionary!)

            //let semaphore = DispatchSemaphore(value: 2)
            group.enter()
            print("Starting Step 3")

            do {
                // Here "jsonData" is the dictionary encoded in JSON data
                let jsonData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)

                // Here "decoded" is of type `Any`, decoded from JSON data
                let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])

                // Final dict
                if let dictFromJSON = decoded as? [String:String] {

                    let endpoint = "http://endpoint.com/user"
                    let url = URL(string: endpoint)
                    let session = URLSession.shared
                    var request = URLRequest(url: url!)

                    request.httpMethod = "POST"
                    request.httpBody = try JSONSerialization.data(withJSONObject: dictFromJSON, options: [])
                    request.addValue("application/json", forHTTPHeaderField: "Accept")
                    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
                    session.dataTask(with: request, completionHandler: { (data, response, error) -> Void in

                        if error != nil {
                            //semaphore.signal()
                            group.leave()
                            print(error)
                            return
                        }

                        do {
                            // Save response
                            let json = try(JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String: AnyObject])

                            if let userID = json?["user_id"] {
                                user.user_id = userID as? Int
                                self.fetchedUser!.setValue(userID, forKey: "user_id")
                            }

                            if let friends = json?["friends"] , !(friends is NSNull){
                                user.friends = friends as? [String]
                                self.fetchedUser!.setValue(friends, forKey: "friends")
                            }

                            group.leave()
                            //semaphore.signal()

                        } catch let jsonError {
                            print(jsonError)
                            return
                        }

                    }).resume()

                }
            } catch {
                print(error.localizedDescription)
            }

            // Wait to async task to finish before moving on
            //_ = semaphore.wait(timeout: DispatchTime.distantFuture)
            print("Step 3 Done")
        }
    }
}

说明:当您执行异步Web请求时,闭包就是所谓的转义,这意味着它们在函数返回后运行.例如, FBSDKGraphRequest.start 进行转义的闭包,保存后返回,然后在 返回后在请​​求完成后运行闭包.这是故意的.否则,它将是同步的并阻塞您的代码,这将导致您的应用程序冻结(除非您使用GCD自己异步运行代码).

Explanation: When you do asynchronous web requests, the closures are what's called escaping, which means they run after the function returns. For example, FBSDKGraphRequest.start takes an escaping closure, saves it, returns, and after it returns, runs the closure once the request finishes. This is intentional. Otherwise, it would be synchronous and block your code, which would cause your app to freeze (unless you use GCD to run the code asynchronously yourself).

TL; DR在诸如 FBSDKGraphRequest.start 之类的函数返回之后调用闭包,从而导致下一个组在完成之前先于下一个组开始.可以通过放置它们以使它们一个接一个地运行来解决此问题.

TL;DR The closures are called after the function such as FBSDKGraphRequest.start returns, causing the next group to start before the one before it finishes. This can be fixed by placing them so that they run one after another.

这篇关于Swift:依次处理多个异步请求.如何等待上一个请求完成?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆