Issue #911

Make an parallelTask function that wraps TaskGroup

public func parallelTask(@ParallelTaskBuilder builder: () -> [ParallelTaskBuilder.Work]) async {
    await withTaskGroup(of: Void.self) { group in
        for work in builder() {
            group.addTask {
                await work.value
            }
        }
    }
}

@resultBuilder
public struct ParallelTaskBuilder {
    public typealias Work = Task<Void, Never>

    public static func buildExpression(_ expression: Work?) -> [Work] {
        if let expression = expression {
            return [expression]
        }
        return []
    }

    public static func buildExpression(_ expression: Work) -> [Work] {
        [expression]
    }

    public static func buildExpression(_ expression: [Work]) -> [Work] {
        expression
    }

    public static func buildBlock(_ components: Work...) -> [Work] {
        components
    }

    public static func buildBlock(_ components: [Work]...) -> [Work] {
        components.reduce([], +)
    }

    public static func buildArray(_ components: [[Work]]) -> [Work] {
        components.reduce([], +)
    }

    public static func buildOptional(_ component: [Work]?) -> [Work] {
        component ?? []
    }

    public static func buildEither(first component: [Work]) -> [Work] {
        component
    }

    public static func buildEither(second component: [Work]) -> [Work] {
        component
    }
}

async let

The child-task created to initialize the async let by default runs on the global concurrent, width-limited, executor that comes with the Swift Concurrency runtime.