Apple documentation says that Operations run synchronously. Why then does the code continue to run after an operation is added to a queue?
Here is my code:
let op = BlockOperation(block: { print("Done") })
let qu = OperationQueue()
qu.addOperation(op)
print("after!")
Here is the debug results:
after!
Done
You said:
Your debug output is correct. The
BlockOperationwill run asynchronously with respect to the thread which added it to the operation queue. The confusing and inconsistent terminology employed by theOperationandOperationQueuedocumentation does not help the matter (see below).But, in short, operations added to an operation queue will run asynchronously with respect to the thread which added them to the queue (unless you explicitly wanted to “wait until finished”, i.e., you supply
truefor the second parameter ofaddOperations(_:waitUntilFinished:); but that is an anti-pattern which we simply do not use very often).I do not know if this is the source of the confusion or not, but there is also a question as to whether the task wrapped by the operation is, itself, synchronous or asynchronous. The Apple documentation refers to these as “non-concurrent” or “concurrent” operations, respectively.
BlockOperationis for “non-concurrent” tasks only (i.e., when the block finishes, theBlockOperationis finished). So, if you stumble across any documentation that refers to the synchronous nature ofBlockOperation, that is in reference to the synchronous, i.e., non-concurrent, nature of the block supplied to the operation, not the broader relationship between theBlockOperationand the thread that added it to the queue. Operations run asynchronously with respect to the thread that added them to the queue (whether the operation, itself, is concurrent or not).FWIW, if you really were wrapping an inherently asynchronous task (such as a network request) within an operation, that called a “concurrent” operation in Apple’s terminology. You would not use
BlockOperationfor a “concurrent” operation. You would subclassOperation, perform the necessary KVO for “concurrent” operations. (If you really want to go tumbling down that rabbit hole, see https://stackoverflow.com/a/40560463/1271826 or https://stackoverflow.com/a/48104095/1271826, but this is largely unrelated to the question at hand.)There is a final notion of “synchronous” vs “asynchronous” within the
OperationandOperationQueuedocumentation. Specifically, the documentation dwells on the terms “asynchronous” and “synchronous” operations in the context of thestartmethod, i.e., operations that are not added to a queue, but are just started immediately. If you callstarton a “synchronous operation” (rather than adding it to a queue), the calling thread will wait. If you callstartan “asynchronous operation”, the calling thread will not wait.I hate to even bring up this idiosyncratic terminology, but only mention for those stumbling through Apple’s documentation. Apple’s use of “synchronous” vs “asynchronous” in this section only applies within the context of the
startmethod. Obviously, we (and Apple, elsewhere in their own documentation) often use these terms more generally.You raised another question:
“Synchronous” simply means that the caller will not proceed until the synchronous code finishes.
Not to split hairs, but it makes no assurances that the synchronous code will execute immediately (though it generally will). It just means that the caller will not proceed until the synchronous call finishes. But if, for example, the synchronously dispatched code is added to a queue that is backlogged, it might not start immediately.
The term “synchronous” also makes no assurances that it will run on the same thread or a different one. Sometimes is will run it on the current thread. Sometimes it will simply wait on the current thread while the synchronous task finishes on some other thread. It depends upon the particular situation.
“Synchronous” only means that the caller will wait, with no assurances about when or where the synchronous code will execute. That’s it.