I have some code here:
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
let types: [FeedTimeline] = [.special, .pr, .pickup, .lastPost]
var operations: [Operation] = []
for type in types {
let operation = BlockOperation {
print("Working")
self.getArticles(of: type, page: 1, completion: { (articles) in
print("Fetched")
})
}
operation.completionBlock = {
print("Done")
}
queue.addOperation(operation)
After I ran the code above, I get the result below:
Working
Done
Working
Working
Done
Done
Working
Done
After a while I got 4 times of "Fetched". Why is that? How to make completionBlock
only run if the API request (getArticles) done.
What I want is: working
-> fetched
-> done
-> working
.... so on
The reason for this behaviour is that the operation continues executing (i.e. exits) once the call to self.getArticles
has been executed, not after it has completed.
One way to fix this is to use dispatch groups. Try the following code:
let queue = OperationQueue()
let group = DispatchGroup()
queue.maxConcurrentOperationCount = 1
let types: [FeedTimeline] = [.special, .pr, .pickup, .lastPost]
var operations: [Operation] = []
for type in types {
let operation = BlockOperation {
group.enter()
print("Working")
self.getArticles(of: type, page: 1, completion: { (articles) in
print("Fetched")
group.leave()
})
group.wait(timeout: DispatchTime.distantFuture)
}
operation.completionBlock = {
print("Done")
}
queue.addOperation(operation)
In this case the call to group.wait() will block until each call to group.enter() is matched by a call to group.leave(), meaning that the BlockOperation will wait until the call to getArticles has completed.
Note that you need to ensure that getArticles always executes the callback, if it doesn't (e.g. due to network timeout), the operation block will hang forever.