Wait for Asynchronous Operation to Complete in Swift

Wait For Asynchronous Operation To Complete in Swift

you have to pass your async function the handler to call later on:

func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
loadShows(completionHandler)
}

func loadShows(completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
//....
//DO IT
//....

completionHandler(UIBackgroundFetchResult.NewData)
println("Background Fetch Complete")
}

OR (cleaner way IMHO)

add an intermediate completionHandler

func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
loadShows() {
completionHandler(UIBackgroundFetchResult.NewData)
println("Background Fetch Complete")
}
}

func loadShows(completionHandler: (() -> Void)!) {
//....
//DO IT
//....
completionHandler()
}

Waiting until the task finishes

Use DispatchGroups to achieve this. You can either get notified when the group's enter() and leave() calls are balanced:

func myFunction() {
var a: Int?

let group = DispatchGroup()
group.enter()

DispatchQueue.main.async {
a = 1
group.leave()
}

// does not wait. But the code in notify() gets run
// after enter() and leave() calls are balanced

group.notify(queue: .main) {
print(a)
}
}

or you can wait:

func myFunction() {
var a: Int?

let group = DispatchGroup()
group.enter()

// avoid deadlocks by not using .main queue here
DispatchQueue.global(attributes: .qosDefault).async {
a = 1
group.leave()
}

// wait ...
group.wait()

print(a) // you could also `return a` here
}

Note: group.wait() blocks the current queue (probably the main queue in your case), so you have to dispatch.async on another queue (like in the above sample code) to avoid a deadlock.

Wait for asynchronous block before continuing

You can use completion closure for this

func a(completion: @escaping (_ value:String)->()) {
var x: String = ""
async block {
x = "test"
completion(x) //when x has new value
}
}

//Call like this (value will be executed when the completion block is returned

a { (value) in
print(value)
}

Wait until swift for loop with asynchronous network requests finishes executing

You can use dispatch groups to fire an asynchronous callback when all your requests finish.

Here's an example using dispatch groups to execute a callback asynchronously when multiple networking requests have all finished.

override func viewDidLoad() {
super.viewDidLoad()

let myGroup = DispatchGroup()

for i in 0 ..< 5 {
myGroup.enter()

Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request \(i)")
myGroup.leave()
}
}

myGroup.notify(queue: .main) {
print("Finished all requests.")
}
}

Output

Finished request 1
Finished request 0
Finished request 2
Finished request 3
Finished request 4
Finished all requests.

Swift: OperationQueue - wait for asynchronous calls to finish before starting next (have maximum of 2 URLRequests running at a time)

Your original code with the operation queue and your original iamgeFromUrl method are all you need if you make one small change to imageFromUrl. You need to add a couple lines of code to ensure that imageFromUrl doesn't return until the download is complete.

This can be done using a semaphore.

public func imageFromUrl(_ urlString: String) {
if let url = URL(string: urlString) {
let request = URLRequest(url: url)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)

let semaphore = DispatchSemaphore(value: 0)
let task = session.dataTask(with: request, completionHandler: {(data, response, error) in
semaphore.signal()
if let imageData = data as Data? {
DispatchQueue.main.async {
self.setImageData(imageData)
}
}
});

task.resume()
semaphore.wait()
}
}

As written now, the imageFromUrl will only return once the download completes. This now allows the operation queue to properly run the 2 desired concurrent operations.

Also note the code is modified to avoid using NSURL and NSURLRequest.

DispatchQueue does not wait for async function to complete

Your problem is that callRestService will dispatch an asynchronous network operation, so your group.leave will be called immediately, firing your group.notify.

You could put the group.leave in a completion handler, but you should avoid blocking code. I would suggest you structure your getTrackingInfo as asynchronous function that takes a completion handler:

public func getTrackingInfo(_ trackingNumber: String, completion:(TrackingInfo?,Error?) -> Void) {
self.callRestService(requestUrl: self.getRequest(trackingNumber)) { (data, response, error) in
guard error == nil, let returnData = data else {
completion(nil,error)
return
}

completion(TrackingInfo(returnData),nil)
}
}

private func getRequest(_ trackingNumber: String) -> String {
let APIUsername = "Intentionally Omitted"
let trackingXmlLink = "http://production.shippingapis.com/ShippingAPI.dll?API=TrackV2&XML=%3CTrackFieldRequest%20USERID=%22" + APIUsername + "%22%3E%20%3CRevision%3E1%3C/Revision%3E%20%3CClientIp%3E127.0.0.1%3C/ClientIp%3E%20%3CSourceId%3EFaiz%20Surani%3C/SourceId%3E%20%3CTrackID%20ID=%22" + trackingNumber + "%22%3E%20%3CDestinationZipCode%3E66666%3C/DestinationZipCode%3E%20%3CMailingDate%3E2010-01-01%3C/MailingDate%3E%20%3C/TrackID%3E%20%3C/TrackFieldRequest%3E"
return trackingXmlLink
}

public func callRestService(requestUrl:String, completion:(Data? , URLResponse? , Error? ) -> Void) ->Void
{
var request = URLRequest(url: URL(string: requestUrl)!)
request.httpMethod = "GET"

let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: completion)

task.resume()
}

How to make async / await in Swift?

Thanks to vadian's comment, I found what I expected, and it's pretty easy. I use DispatchGroup(), group.enter(), group.leave() and group.notify(queue: .main){}.

func myFunction() {
let array = [Object]()
let group = DispatchGroup() // initialize

array.forEach { obj in

// Here is an example of an asynchronous request which use a callback
group.enter() // wait
LogoRequest.init().downloadImage(url: obj.url) { (data) in
if (data) {
group.leave() // continue the loop
}
}
}

group.notify(queue: .main) {
// do something here when loop finished
}
}


Related Topics



Leave a reply



Submit