Dependencies using async task

How can I improve dependencies here where Employee object in for loop depends on getAddress api to get employee address and keeps entire for loop on wait state until getAddress is finished?

I would like to make getAddress Api call concurrent and move to next forEach employee record without waiting for getAdress to return. And finally return all [Employee] array after all requests finished.

func employees() async throws -> [Employee] {
    var employees = [Employee]()

    let employeeInfoArr: [EmployeeInfo] = try await getEmployeeInfoRecords()
    employeeInfoArr.forEach { employeeInfo in
        let employeeAddress = try await getAddress(for: employeeInfo.name) // 0...3 seconds each for request
        let employee = Employee(name: employeeInfo.name, address: employeeAddress) // ForEach waits until we receive response and then move to next request
        employee.name.append(employee)
    }

    return employees
}

Would AsyncSequence’s async map be helpful in this situation?

It would be nice if whatever API backing getAddress(..) offered a form that could return addresses for multiple employees in one query. If that’s not available, but the API supports your method using potentially dozens of concurrent connections, I’d expect a nice speed boost using withThrowingTaskGroup(of:returning:body:):

func employees() async throws -> [Employee] {
    let employeeInfoArr: [EmployeeInfo] = try await getEmployeeInfoRecords()
    
    return try await withThrowingTaskGroup(of: Employee.self) { taskGroup in
        employeeInfoArr.forEach { employeeInfo in
            taskGroup.addTask {
                let employeeAddress = try await getAddress(for: employeeInfo.name)
                return Employee(name: employeeInfo.name, address: employeeAddress)
            }
            
        }
        
        var employees = [Employee]()
        
        for try await employee in taskGroup {
            employees.append(employee)
        }
        
        return employees
    }
}

*It's important to note the results from employees() will not be in the same order as the elements of employeeInfoArr since the task group's tasks are ran concurrently, then collected according to which tasks finished processing first.

1 Like

Yeah, this is the solution. Thanks @christopherweems!

Also, is it possible to limit the number of active tasks in a taskgroup like semaphore so that the next submitted tasks are executed after the submitted_tasks_count < allowed_task_limit_count?

Would swift-async-algorithms’s throttle functions help with that? I don’t see a way to throttle by task count rather than a time interval, though.

Yes, you just do it manually. Enqueue limit tasks first, then loop over the results, enqueuing another for each completion to to count.

1 Like

Yes @Jon_Shier, thank you.