How to Tell If Blocks in Loop All Have Completed Executing

How to tell if blocks in loop all have completed executing?

Create a dispatch group, in the for loop enter the group, in the completion block leave the group. Then you can use dispatch_group_notify to find out when all blocks have completed:

dispatch_group_t    group = dispatch_group_create();

for (PFObject *pictureObject in objects){

PFFile *imageFile = [pictureObject objectForKey:@"image"];
NSURL *imageFileURL = [[NSURL alloc] initWithString:imageFile.url];
NSURLRequest *imageRequest = [NSURLRequest requestWithURL:imageFileURL];

dispatch_group_enter(group);
[tokenImageView setImageWithURLRequest:imageRequest placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
[self.downloadedUIImages addObject:image]; //This is a mutableArray that will later be set to an UIImageView's animnationImages
dispatch_group_leave(group);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(@"Error %@", error);
dispatch_group_leave(group);
}];

}

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// do your completion stuff here
});

Loop through block while waiting for previous execution to finish

Try this

    dispatch_semaphore_t sema = dispatch_semaphore_create(0);

for (NSString *myString in myArray) {

[self doSomethingToString:myString WithCompletion:^(BOOL completion) {
string = [NSString stringByAppendingString:@"Test"];
dispatch_semaphore_signal(sema);
}];

dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_release(sema);

}

Swift - How to know when a loop has ended?

To produce the same result as what others have posted but with basic closure syntax:

func printFunction() {
println("loop has finished")
}

func loopWithCompletion(closure: () -> ()) {
for var i=0; i<5; i++ {
println(i)
}
closure()
}

This is how you call it:

 loopWithCompletion(printFunction)

Swift 3 Update:

func printFunction() {
print("loop has finished")
}

// New for loop syntax and naming convention
func loop(withCompletion completion: () -> Void ) {
for i in 0 ..< 5 {
print(i)
}
completion()
}

Then call it like this:

loop(withCompletion: printFunction)

Or

loop {
print("this does the same thing")
}

If I have a for loop that uses Obj-C blocks, how can I identify the last iteration?

The pointer comparison (objectID == [objectIDs lastObject]) is alright here as you will get the same objects if the array is not modified. If you are concerned that objectIDs might get modified (e.g. that it might actually be a mutable array and get modified on another thread or through side effects like notifications along the way), the best protection would be to copy the array right at the beginning:

- (void)fetchObjectsFromServerWithObjectIDs:(NSArray*)objectIDs {

typeof(self) __weak blockSelf = self;
NSArray *copiedIDs = [objectIDs copy];

for(NSString *objectID in copiedIDs) {
[self fetchObjectDataForObjectID:objectID withSuccessBlock:^{
if(objectID == [copiedIDs lastObject]) {
[blockSelf.delegate finishedFetching];
}
}];
}
}

There are now two ways to call the delegate:

  1. Call it once the last fetch has finished.
  2. Or call it once the fetch for the last object has finished, even if other fetch requests are in progress.

So you can:

  • Use a counter like @ChrisH proposed. This will call the delegate after the very last fetch has finished, no matter in which order they were processed.
  • Use the pointer comparison. This will call the delegate after the fetch for lastObject has finished.
    • For this, I think there's a minor optimization you can do. As it is, it has the downside that it captures the array in the block and invokes an unnecessary method call every time the block is called. Instead, you can save the last object in an variable, so that the last object instead of the whole array is captured by the block. Note that capturing the array by the block doesn't do any harm. It might just defer freeing some memory.

So you could do (@ChrisH's version plus copy):

- (void)fetchObjectsFromServerWithObjectIDs:(NSArray*)objectIDs {

typeof(self) __weak blockSelf = self;
NSArray *copiedIDs = [objectIDs copy];
__block NSUInteger count = [copiedIDs count];

for(NSString *objectID in copiedIDs) {
[self fetchObjectDataForObjectID:objectID withSuccessBlock:^{
if(--count == 0) {
[blockSelf.delegate finishedFetching];
}
}];
}
}

or:

- (void)fetchObjectsFromServerWithObjectIDs:(NSArray*)objectIDs {

typeof(self) __weak blockSelf = self;
NSArray *copiedIDs = [objectIDs copy];
id lastObject = [copiedID lastObject];

for(NSString *objectID in copiedIDs) {
[self fetchObjectDataForObjectID:objectID withSuccessBlock:^{
if(objectID == lastObject) {
[blockSelf.delegate finishedFetching];
}
}];
}
}

How to ensure that a statement gets executed after a loop completes?

exports.index = function(req, res) {
var events = require("events");
var e = new events.EventEmitter();
e.expected = 1000;
e.finished = 0;
e.results = [];

e.on("finishedQuery", (function(err, r) {
this.finished += 1;
this.results.push(r && r.name);
if (this.finished === this.expected) {
res.render('index', { title: 'Home !' , results:this.results });
};
}).bind(e));

for (var i = 0; i < e.expected; i++) {
client.hgetall("game:" + i, function(err, reply) {
e.emit("finishedQuery", err, reply);
});
};
};

Of course, the above code doesn't handle [1 or more] errors. You'd need to add logic that only responds (1) on the first error or (2) if no errors occur.

How to check if the for loop is interrupted in Python

In python, you can use the for/else statement. You can add an else statement after a for loop and if break was not called, the else statement will trigger.

You can use this to set a flag that the for loop finished.

Example:

for i in range(10)
if i == 10:
break
else:
print('the for loop ran without breaking')

The above code will print the text 'the for loop ran without breaking', whereas the code below will not.

for i in range(10)
if i == 5:
break
else:
print('the for loop ran without breaking')

Execute code after executing everything for loop

Here you go.

//Change method to async
async save() {
//Create custom index variable, since for of loop doesn't have one
let ind = 0;
//Use for of instead of forEach
for (const eachRow of this.selectRowData) {
//await until API validated
const isValid = await this.myService.validate(eachRow.id).toPromise();
if (!isValid) {
//await again if it is not valid and until user confirmed
const isConfirmed = await this.myConfirmService.confirm('Please confirm..', 'Selected Part do not match expected part data. \n Do you still want to continue? ', 'Yes', 'No').toPromise();
if (isConfirmed) {
//await again until record saved as user selected yes in confirm
console.log('User selected: Yes');
await this.myService.savePart().toPromise();
} else {
console.log('User selected: No');
}
} else {
console.log(
'Selected Rule matches expected rules for: ' +
eachRow.designChangeClass
);
this.saveModelPart(eachRow);
}
//Finally display message after loop is finished
if (ind === this.selectRowData.length - 1) {
this.alertService.success('Part saved successfully.');
}
ind++;
}
}

Although this solution works but is not a good practice to do it in this way,

Instead, modify API to accept multiple ids for validation, and display a table of records (success and failures) along with buttons to confirm for continuation.

Why does python use 'else' after for and while loops?

It's a strange construct even to seasoned Python coders. When used in conjunction with for-loops it basically means "find some item in the iterable, else if none was found do ...". As in:

found_obj = None
for obj in objects:
if obj.key == search_key:
found_obj = obj
break
else:
print('No object found.')

But anytime you see this construct, a better alternative is to either encapsulate the search in a function:

def find_obj(search_key):
for obj in objects:
if obj.key == search_key:
return obj

Or use a list comprehension:

matching_objs = [o for o in objects if o.key == search_key]
if matching_objs:
print('Found {}'.format(matching_objs[0]))
else:
print('No object found.')

It is not semantically equivalent to the other two versions, but works good enough in non-performance critical code where it doesn't matter whether you iterate the whole list or not. Others may disagree, but I personally would avoid ever using the for-else or while-else blocks in production code.

See also [Python-ideas] Summary of for...else threads



Related Topics



Leave a reply



Submit