Post not yet marked as solved
I am a bit confused about tasks being cancelled.
Overview:
checkCancellation function has 2 child tasks: computeA and computeB that run concurrently,
computeB throws an error.
Doubt:
I expected child task computeA to be cancelled because computeB threw an error, but computeA was never cancelled.
Is my understanding wrong or am I missing something?
Or is this a bug?
Note:
I am using a SwiftUI project (as Swift Playgrounds don't support async let)
macOS Big Sur 11.5.2 (20G95)
Xcode Version 13.0 beta 5 (13A5212g)
Output:
A - started
B - going to throw
A - going to return, Task.isCancelled = false
error: infinity
Concurrent Function Definitions:
import Foundation
import UIKit
enum ComputationError: Error {
case infinity
}
fileprivate func computeA() async throws -> Int {
print("A - started")
await Task.sleep(2 * 100_000_000)
print("A - going to return, Task.isCancelled = \(Task.isCancelled)") //I expected Task.isCancelled to be true
return 25
}
fileprivate func computeB() async throws -> Int {
print("B - going to throw")
throw ComputationError.infinity
}
func checkCancellation() async throws {
async let a = computeA()
async let b = computeB()
let c = try await a + b
print("c = \(c)")
}
Invoking Concurrent function
struct ContentView: View {
var body: some View {
Button("check cancellation") {
Task {
do {
try await checkCancellation()
print("normal exit")
} catch {
print("error: \(error)")
}
}
}
}
}
Post not yet marked as solved
I'm trying to use the new structured concurrency features of Swift 5.5 to update some code in a larger App that has been written using GCD. One routine is loading parts of a dataset in a parallel loop and I would like to convert this code using withThrowingTaskGroup while keeping a progress counter. The code compiles, runs, but does not update the progress in a way I expected:
var progressCounter = 0
let count = 500
try await withThrowingTaskGroup(of: Void.self) { group in
for idx in 0..<count {
_ = group.addTaskUnlessCancelled {
// some IO
}
}
for try await _ in group {
progressCounter += 1
}
}
The for try await loop is executed after all 500 task have been run and finished (iOS 15 Beta 5). I was under the impression, the for try await loop would be run in parallel to the tasks, collecting results as they come in and thereby updating the progress counter. Clearly I'm missing something or is this expected? Is withThrowingTaskGroup not the right tool for this?
Any insight would be appreciated.
Post not yet marked as solved
I've had an issue in my project where I trigger a task from a SwiftUI View with the modifier .task. From there it calls and async function into an actor that has a TaskGroup that makes concurrent network calls that has a type of Void.self. I've noticed that sometimes the TaskGroup is either never called and will no longer be called.
I'm wondering if perhaps a TaskGroup inside of an Actor that has a parent Task created outside the Actor is a problem? But it does seem to work if I change the Actor to a Class. Could someone help explain to me why this is?
Post not yet marked as solved
I'm seeing an error trying to test out async let. It seems like this should work, based on the async/let and structured concurrency session videos.
Here's the code:
func doIt() async -> String {
let t = TimeInterval.random(in: 0.25 ... 2.0)
Thread.sleep(forTimeInterval: t)
return String("\(Double.random(in: 0...1000))")
}
async {
async let a = doIt()
async let b = doIt()
async let c = doIt()
async let d = doIt()
let results = await [a, b, c, d]
for result in results {
print(" \(result)")
}
}
But, I get this error for every "async let" statement:
error: AsyncLetSwift55WWDC21.playground:12:15: error: expression is 'async' but is not marked with 'await'
async let a = doIt()
^
await
Am I missing something key, or is this a bug?
I'm running this in the Xcode 13.0 beta, in a Playground.
Thanks!!
Post not yet marked as solved
Hi,
We have an API that I'm trying to prototype some async await extensions for. Most of our asynchronous methods are cancelable by returning an object that implements our AGSCancelable protocol that includes a cancel() method.
Here's the code I have to try to extend a geocoding method on our AGSLocatorTask class:
extension AGSLocatorTask {
func geocode(withSearchText text: String) async throws -> [AGSGeocodeResult] {
typealias GeocodeContinuation = CheckedContinuation<[AGSGeocodeResult], Error>
var cancelable: AGSCancelable?
return try await withTaskCancellationHandler(handler: {
cancelable?.cancel()
}, operation: {
return try await withCheckedThrowingContinuation({ (continuation: GeocodeContinuation) in
cancelable = self.geocode(withSearchText: text) { results, error in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: results!)
}
}
})
})
}
}
This works great, but I have to comment out the cancelable?.cancel() call or else I get a Reference to captured var 'cancelable' in concurrently-executing code static analysis error (see attached screenshot).
Could someone explain how to fix this, or is this a bug in the Xcode beta?
Many thanks, and thanks for a great WWDC!!