Swift:按顺序多个异步请求。如何等待上一个请求完成?

2023-12-29

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

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

认证控制器

 // 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")
        }
    }
}

将每个闭包后面的代码移到闭包本身内,以便它等到它前面的代码再运行:

// 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接受一个转义闭包,保存它,返回,然后after它返回,并在请求完成后运行闭包。这是故意的。否则,它将是同步的并阻止您的代码,这将导致您的应用程序冻结(除非您自己使用 GCD 异步运行代码)。

TL;DR 闭包在函数之后调用,例如FBSDKGraphRequest.start返回,导致下一组在完成前的一组之前开始。可以通过放置它们使它们依次运行来解决此问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Swift:按顺序多个异步请求。如何等待上一个请求完成? 的相关文章

随机推荐

  • 用GDB读取寄存器指向的内存

    如果我知道内存位置 有没有办法从 GDB 查看内存内容 也就是说 我正在调试我为操作系统课程编写的 x86 汇编程序 现在 我正在尝试使用 C 和 Gas GNU 汇编程序 为 x86 上的 Linux 编写一个用户级线程库 我分配了自己的
  • PHPStorm 中未定义的类 DateTime

    我想使用类DateTime but PHPStorm我这么说 Undefined class DateTime 我的 PHP 版本是5 5 9 1 我使用的是 PHPStorm 版本2016 1我正在与Yii2 framework 如果它能
  • 在 R 中对整齐数据同时执行多个 t.test

    我有一个如下所示的数据集 id samediff factor value 1 S give 3 1 S impact 4 2 S give 2 2 S impact 5 3 D give 1 3 D impact 4 4 D give 3
  • 如何向 Kivy For Android 添加模块?

    我在 Android 设备上安装了 Kivy 但 help modules 显示未安装 PIL 模块 我怎样才能添加它 编辑 我的意思是 我安装了 kivy launcher 并编写了一个小型测试应用程序 它可以工作 现在 如果我的应用程序
  • 当我单击 li 标签时,它会获取数据值,但是当我插入它时,它具有空值

    单击时我得到 li 标签数据值 现在我需要使用插入回数据库的值 但它在我的数据库中显示空值 但我使用 console log 它显示了价值 如何获取值并插入数据库 mymoviemanagement php movie 的值为 null i
  • 如何在 PHP 中设置 cookie 然后重定向?

    进行一些处理后 我想为用户输入设置一个 cookie 值 然后将它们重定向到新页面 但是 cookie 尚未设置 如果我注释掉重定向 则 cookie 设置成功 我认为这是某种标题问题 对于这种情况 最好的解决方法是什么 if form s
  • 不同大小类别的不同表视图单元格行高?

    如何更改此 UITableViewController 自定义类以动态更改表视图单元格的高度 我为 iPad 和 iPhone 尺寸类别指定了不同的字体大小 这是之前与 rdelmar 讨论的延续 import CREWFoodWaterL
  • 计算POST内容长度

    如何计算内容长度 例如 POST Upload HTTP 1 1 Host test lan User Agent Shockwave Flash Connection Keep Alive Cache Control no cache A
  • 操作栏抽屉切换自定义图标

    我正在尝试使用操作栏抽屉切换 但我希望它显示的不仅仅是菜单图标 我希望它在自定义菜单图标的右上角显示通知计数 但我仍然希望操作栏抽屉式开关在需要时显示后退箭头 因此 我首先尝试查看是否可以在操作栏抽屉切换上显示自定义布局 包含菜单图标的图像
  • Guid.NewGuid() VS Random.Next() 的随机字符串生成器

    我和我的同事正在讨论使用哪种方法来自动生成用户 ID 和帖子 ID 以在数据库中进行识别 一个选项使用 Random 的单个实例 并采用一些有用的参数 以便它可以重用于各种字符串生成情况 即从 4 位数字 pin 到 20 位字母数字 id
  • 如何使用JPA或HQL动态排序多对多关系?

    我有一个这样的映射 ManyToMany cascade CascadeType PERSIST JoinTable name product product catalog joinColumns JoinColumn name prod
  • 访问 Android 上先前安装创建的文件 - 范围存储 Android 11

    我正在使用 LibGDX 开发一个带有预设管理器的音乐应用程序 该管理器使用 LibGDX 的 FileHandle 来管理文件和目录 在我更新到 Android 11 API 30 之前 此功能一直运行良好 现在由于 Android 新的
  • Ruby OptionParser:隐藏命令选项的帮助文本

    Ruby OptionParser 将根据此描述自动为您生成帮助屏幕 http ruby about com od advancedruby a optionparser htm 有没有办法删除命令选项的帮助文本 我可以使用隐藏命令 而是使
  • 访问 Ruby 中受保护的方法

    我正在尝试在 Ruby 中为自己使用访问修饰符 我有 class Person def initialize first name last name age first name first name last name last nam
  • 全球之前一切都是开玩笑?

    我需要使用 React 测试库和 Jest 在测试中模拟不同的窗口大小 目前我必须拥有这个beforeAll在每个测试文件中 import matchMediaPolyfill from mq polyfill beforeAll gt m
  • 数据表行选择不起作用

    我有一个数据表
  • 在 JQueryMobile 中使用 autoInitializePage 的示例

    我正在寻找一个简单的示例 展示如何使用 JQueryMobile 呈现已在客户端动态组装的页面 mobile autoInitialize 曾经在几个版本前工作过 现在我们有 mobile autoInitializePage 但我似乎无法
  • jQuery UI - 按钮集按钮并不总是在第一次单击时起作用

    我正在使用 jQuery UI 按钮集 基于一些单选按钮 一切都工作得很好 除了有时当您单击其中一个按钮时什么也没有发生 就像您根本没有单击过它一样 我认为这可能是我的实现的问题 所以我去了演示站点 http jqueryui com de
  • 从 ec2 到 s3 的大文件

    我有一个 27GB 的文件 正在尝试将其从 AWS Linux EC2 移动到 S3 我尝试过 S3put 命令和 S3cmd put 命令 两者都使用测试文件 两者都不适用于大文件 没有给出错误 命令立即返回 但没有任何反应 s3cmd
  • Swift:按顺序多个异步请求。如何等待上一个请求完成?

    作为我的应用程序中身份验证过程的一部分 用户可以使用他们的 Facebook 帐户登录 我正在使用 Facebook iOS SDK 来处理此过程 身份验证完成后 我向 Facebook 图形 api 发出请求以获取用户个人资料数据 这是第