【问题标题】:Trouble returning a variable from within a completion block:从完成块中返回变量时遇到问题:
【发布时间】:2016-06-17 18:19:14
【问题描述】:

我正在使用以下函数通过 RESTEngine.sharedEngine.registerUser 函数从我的服务器获取一些数据。 “userCreationCall”进行 API 调用,解析它返回的 JSON,创建一个用户,然后将该用户传递到其完成块中。

“makeUser”然后通过将其中的用户传递给另一个函数来处理该完成块,该函数返回用户对象供我在我的应用程序的其他地方使用。

func userCreationCall(first: String, last: String, email: String, completionHandler: (User?, ErrorType?) -> ()) {
RESTEngine.sharedEngine.registerUser(email, firstName: first, lastName: last, age: 12, success: { response in
    if let response = response, result = response["resource"], id = result[0]["_id"] {            
        let params: JSON =
        ["name": "\(first) \(last)",
            "id": id as! String,
            "email": email,
            "rating": 0.0,
            "nuMatches": 0,
            "nuItemsSold": 0,
            "nuItemsBought": 0]
        let user = User(json: params)
        completionHandler(user, nil)
    }
    }, failure: { error in
        completionHandler(nil, error)
    })
}

func makeUser(first: String, last: String, email: String) {

    userCreationCall(first, last: last, email: email) { user, error in
        guard error == nil else {
            print("Error creating a user! : \(error)")
            return
        }

        returnUser(user!)
    }
}

func returnUser(user: User) -> User {
    return user
}

出于测试目的,我在 viewDidLoad 中进行了以下调用,旨在为“testUser”分配在“makeUser”的完成块中传递的用户对象:

override func viewDidLoad() {
    super.viewDidLoad()  
    let testUser = makeUser("John", last: "Doe", email: "johnDoe@gmail.com")
    print("\(testUser)")
}

但是,当我打印 testUser 时,它仍然是空白的(这意味着它永远不会在“makeUser”的完成块中分配给用户对象)。有没有人知道我的逻辑/实现哪里出错了?提前致谢!

【问题讨论】:

  • 您也应该在makeUser 中使用完成处理程序来处理您想要的输出,而不是返回。
  • 您从that other answer 中获取了makeUser,但似乎没有理解基本点,即您不能从异步方法返回值(并且getUser 现在是异步方法) .调用异步方法时,不要尝试返回结果,而是始终遵循异步编程模式,例如完成处理程序模式。
  • 您说“它永远不会在“makeUser”的完成块中分配用户对象”。不,它确实得到了一个值,但稍后,在 viewDidLoad 已经完成之后。您只能在完成块中使用该值,而不能在 viewDidLoad 中使用。
  • 所以由于我无法从异步方法返回值,看来我唯一剩下的选择是在异步调用之前声明一个“空”用户对象,我将用数据填充我从回应中得到。我的想法在这里正确吗?
  • 是的,就是这样做的。如果可以,请保存上次的内容,以便在使用更新的数据进行刷新时显示一些内容。或者在执行异步请求时显示活动指示器视图。

标签: ios swift asynchronous closures


【解决方案1】:

例如,您还需要在 makeUser 中使用完成块

func makeUser(first: String, last: String, email: String, completion:((User?) -> Void)) {

  userCreationCall(first, last: last, email: email) { user, error in
    guard error == nil else {
        print("Error creating a user! : \(error)")
        completion(nil)
        return
    }

    completion(user)
  }
}

override func viewDidLoad() {
    super.viewDidLoad()  
    makeUser("John", last: "Doe", email: "johnDoe@gmail.com") { user in 
       if user != nil {
          print("\(user!)")
       }
    }
}

但实际上不需要额外的功能

override func viewDidLoad() {
    super.viewDidLoad()

    userCreationCall("John", last: "Doe", email: "johnDoe@gmail.com") { user, error in
      if error != nil {
         print("Error creating a user! : \(error!)")
      } else {
         print("\(user!)")
         // do something with the new created user
      }
   }
}

【讨论】:

  • 所以我想让我感到困惑的是如何使用从 userCreationCall(即用户)获得的信息在我的应用程序上创建本地用户变量?我是否需要在调用“userCreationCall”之前创建一个“空”未初始化用户,然后在其完成块中使用调用中的数据为我的“空”用户分配其属性?
  • 没错,由于数据是异步检索的,因此您必须在完成块中分配值。
  • 好吧,很公平。我仍在为此苦苦挣扎,因为我正在尝试找到一种将“用户返回”合并到这些块中的方法。基本上我想要实现的是让我的前端人员能够调用“makeUser”,传递一些基本信息,并从服务器取回使用数据获取的用户对象,而不必担心完成块或后端实现。关于如何实现这一点的任何建议?
  • 尝试理解并习惯异步模式。从服务器加载数据总是需要一些时间,为了获得良好的用户体验,您必须避免同步执行,例如会阻塞 UI。总有一种方法可以异步返回数据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多