问题
I need to wait until Alamofire request finishes getting data. (Error or value). I'm calling Alamofire function inside a for loop in another function so that Alamofire requests should be finished before second for loop called. For example; first loop -> first request -> second loop -> second request...so on. Now It goes first loop -> second loop -> and after all loops finishes requests response is turning.
Request Function:
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"
}
}}
}
}
Where I call:
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()
}
回答1:
You are using dispatch group, obviously with the intent on waiting for the group at the end of the function. But you're not waiting for it, so you're not getting the synchronous behavior you were looking for.
But that's good, because if you wait
for that group (or semaphore, the other pattern to achieve this behavior) on the main thread, not only will you be blocking the main thread (which results in horrible UX and risk having your app killed by watchdog process), you're going to deadlock because responseJSON
uses the main queue for it's completion handler. So before you add the wait()
call on your dispatch group/semaphore, make sure you dispatch this whole for
loop asynchronously to some background thread. That avoids blocking the main thread and eliminates the deadlock risk.
But this whole pattern is fundamentally flawed, as you really shouldn't use dispatch groups or semaphores to make it synchronous at all. That raises a few questions:
The first question is why you want to make this synchronous. Network requests have inherent latency, so you performing a sequence of them will be very slow. Only do this sequence of requests if you absolutely have to (e.g. each request cannot be formed because it needs something from the response of the prior request). But that doesn't appear to be the case here. So why make this process unnecessary so.
But let's assume for a second that you absolutely have to perform these sequentially (not true here, from what I can tell, but let's tease this out). Then there are two patterns for performing a series of requests consecutively rather than concurrently:
You can either lose this
for
loop entirely, and simply have a routine that sends the n-th request and sends request n+1 in its completion handler. That completely eliminates the need for dispatch groups/semaphores to block a thread.Or you can wrap this in operation (e.g. https://stackoverflow.com/a/27022598/1271826) and use operation queue.
回答2:
I solve it by calling like function with index + 1 everytime Alamofire returns value. I also call function in completion part of fetch request.
Here is the code:
@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)
})
}
})
}
Like Function:
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}
}}}
来源:https://stackoverflow.com/questions/47718265/synchronous-alamofire-request-with-dispatchgroup