Wait Until Multiple Networking Requests Have All Executed - Including Their Completion Blocks

Wait until multiple networking requests have all executed - including their completion blocks

Use dispatch groups.

dispatch_group_t group = dispatch_group_create();

MyCoreDataObject *coreDataObject;

dispatch_group_enter(group);
AFHTTPRequestOperation *operation1 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation1 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
coreDataObject.attribute1 = responseObject;
sleep(5);
dispatch_group_leave(group);
}];
[operation1 start];

dispatch_group_enter(group);
AFHTTPRequestOperation *operation2 = [[AFHTTPRequestOperation alloc] initWithRequest:request1];
[operation2 setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
coreDataObject.attribute2 = responseObject;
sleep(10);
dispatch_group_leave(group);
}];
[operation2 start];

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);

[context save:nil];

Wait until multiple request to be executed and their completion block

What you need is something called resource locking. You can achieve this by using DispatchGroup.

First you need to create a DispatchGroup. Add a property in your controller:

let dispatchGroup = DispatchGroup()

Then modify your refresh(param:) function as: (I've modified some of the coding patterns)

func refresh(param: NSInteger) -> Void {
// You lock your resource by entering to the dispatch group
dispatchGroup.enter()
let absolutePath = "MY SAMPLE API"
var headers = [String: String]()
headers["Content-Type"] = "application/json"
print("Entered \(param)")
Alamofire.request(absolutePath, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: headers).responseString { [weak self] (response) in
switch response.result {
case .success:
print("SUCCESS \(param)")
break
case .failure(let error):
print(error)
}
// You release the resource as soon as you get the response so that other processes may be able to use the resource
self?.dispatchGroup.leave()
}
// The lock continues by invoking the wait method
dispatchGroup.wait()
}

So, this will work as:

Method 1 & Method 2 are requesting to use the same resource. When Method 1 is executing, Method 2 will wait for Method 1 to finish. When Method 1 is finished, Method 2 will be given the opportunity to start it's execution.

So, basically which method first starts executing will finish and then the other will be started. Though it's not guaranteed which will start the execution first (As, you don't need dependency on each other). But it will depend on the sequence you invoke this method.

Wait for multiple AFNetworking requests to complete

The dispatch queue for completionBlock. If NULL (default), the main queue is used.

dispatch_group_t group = dispatch_group_create();

dispatch_queue_t queue = dispatch_queue_create("com.app", DISPATCH_QUEUE_CONCURRENT);

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.completionQueue = queue;
for(int k = 0; k < 10; k++) {
dispatch_group_enter(group);

dispatch_async(queue, ^{
NSLog(@"%d", k);
[manager GET:@"http://baidu.com/" parameters:nil progress:nil success:^(NSURLSessionDataTask *_Nonnull task, id _Nullable responseObject) {
NSLog(@"success");
dispatch_group_leave(group);
} failure:^(NSURLSessionDataTask *_Nullable task, NSError *_Nonnull error) {
NSLog(@"failure");
dispatch_group_leave(group);
}];
});
}

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

NSLog(@"DONE");

Need to make two HTTP network requests simultaneously (with a completion handler once both finish)

You should use dispatch groups, entering the group before you issue the request, and leaving the group in the completion handler for the request. So, let's assume, for a second, that you had some method that performed an asynchronous request, but supplied a completion handler parameter that was a closure that will be called when the network request is done:

func perform(request: URLRequest, completionHandler: @escaping () -> Void) { ... }

To start these two concurrent requests, and be notified when they're done, you'd do something like:

let group = DispatchGroup()

group.enter()
perform(request: first) {
group.leave()
}

group.enter()
perform(request: second) {
group.leave()
}

group.notify(queue: .main) {
print("both done")
}

Clearly, your implementation of perform(request:) may vary significantly (e.g. you might have the closure pass the data back), but the pattern is the same whether you are writing your own networking code with URLSession or using Alamofire. Just use GCD groups, entering the group when you create the requests, and leaving the group in the completion handler of the asynchronous request.

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.

How to wait until a async call complete, including completion block (AFNetworking)

You're probably not getting any output because your program is never moving past the call to dispatch_group_wait. If it did, then you'd see the "outside" log statement.

If dispatch_group_wait is never returning, then there must still be something in the group. In your sample code, you add one thing to the group with dispatch_group_enter and then remove it in either the success or failure handler for the api call with dispatch_group_leave. This means that dispatch_group_leave is not being called for some reason.

My suspicion is that the reason the blocks are not being called is that they will be invoked asynchronously on the same dispatch queue that your outer code runs on. If this is the case, then they can't run until dispatch_group_wait returns and dispatch_group_wait cannot return until the blocks run. This is called deadlock. (Edit: Alternatively, it could be that some part of the program that invokes the success or failure blocks is the part that is leading to deadlock. Either way, the result is that the blocks can't get called since dispatch_group_wait never returns.)

The other possibility is that the method -dealWithZTStrangeJSON: never returns for some reason. If this is the case, then the success block will be invoked (you could set a breakpoint on its first line to verify), but it will never make it to dispatch_group_leave.

In either case, I would recommend that you think about solving your problem another way instead of waiting for the operation to finish. Perhaps you can do the things that you were planning to do after dispatch_group_wait returns inside of the success handler instead (or another way of thinking about it would be that the success or failure handler could call a method that does the things you're currently doing after dispatch_group_wait—either way will work, but sometimes I find that it's easier to keep my code organized by calling out to a method instead of putting all of the code in a block. This could be especially useful if you want to share some code between the success and failure blocks).

Wait until all iOS blocks are executed before moving on

I played around with dispatch groups the other day, here is a really helpful blog post by jrturton which should help you with the basics!

But basically it looks like your missing the line to enter/leave the dispatch group. So non of your runAPI methods are being run as you have no items in the group and [[NSNotificationCenter defaultCenter] postNotificationName:@"apiDataUpdated" object:self]; gets called straight away.

dispatch_group_t group = dispatch_group_create();

for(MyAPIEndpoint __weak __block *ep in apiList)
{
dispatch_group_enter(group);
[self runAPI:ep withCompletionBlock:^(MyAPIEndpoint *api, NSError *err)
{
// update the data model here, code omitted as it's not relevant
dispatch_group_leave(group);
}];
}
});

dispatch_group_notify(group, dispatch_get_main_queue(),^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"apiDataUpdated" object:self];
});

Waiting until two async blocks are executed before starting another block

Use dispatch groups: see here for an example, "Waiting on Groups of Queued Tasks" in the "Dispatch Queues" chapter of Apple's iOS Developer Library's Concurrency Programming Guide

Your example could look something like this:

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block1
NSLog(@"Block1");
[NSThread sleepForTimeInterval:5.0];
NSLog(@"Block1 End");
});


dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block2
NSLog(@"Block2");
[NSThread sleepForTimeInterval:8.0];
NSLog(@"Block2 End");
});

dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block3
NSLog(@"Block3");
});

// only for non-ARC projects, handled automatically in ARC-enabled projects.
dispatch_release(group);

and could produce output like this:

2012-08-11 16:10:18.049 Dispatch[11858:1e03] Block1
2012-08-11 16:10:18.052 Dispatch[11858:1d03] Block2
2012-08-11 16:10:23.051 Dispatch[11858:1e03] Block1 End
2012-08-11 16:10:26.053 Dispatch[11858:1d03] Block2 End
2012-08-11 16:10:26.054 Dispatch[11858:1d03] Block3

How to know now when multiple server call methods with nested loops have all finished

Didn't want to have to answer my own question... don't really like doing that, and was hoping for a more elegant solution... but it doesnt seem to be coming and someone even downvoted the question. So i'm just going to post my solution and close the question out. If you end up with a similar problem and are reading this, I dealt with it by:

creating 2 int's for each method. One int is immediately set with the count of the array being iterated through. at the end of each iteration, I am posting an NSNotification. In the NSNotification handler method, i am ++ incrementing the second int & each time running an if condition, checking to see if they match.

It's a pain to keep track of all these when there are many methods like this happening... So if anyone ever finds a better solution, I'd love to hear it. Thanks to everyone who answered and tried to be helpful, I appreciate it!

How to make multiple GET request and to wait that all operations are done?

I found a good solution with AFNetworking, and I could do exactly what I wanted to do :

NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:mutableOperations progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
NSLog(@"%lu of %lu complete", numberOfFinishedOperations, totalNumberOfOperations);
} completionBlock:^(NSArray *operations) {
[self.delegate dataRefreshingDone];
}];
[[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];

It will execute all my GET operations one by one.
I made a special getter to create an operation according a url .

- (AFHTTPRequestOperation *)operationGetForUrl:(NSString *)url
{
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/api/%@", [NSString stringWithUTF8String:kBaseURL], url]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
[request setHTTPMethod:@"GET"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation.responseSerializer setAcceptableContentTypes: [operation.responseSerializer.acceptableContentTypes setByAddingObject:@"text/html"]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// YOUR SUCCESS BLOCK
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// FAILURE BLOCK
}];
return operation;
}

So as you see, for each operation, I have a success block, its possible to make some generic things ... Or you can make specifics operation getter.



Related Topics



Leave a reply



Submit