Зачем нам нужен новый подход ?
Structured concurrency (далее SC) позволяет рассуждать о конкуретном вычислении используя специальные точки, позволяет узнать о разветвлениях, конкурентных вычислениях и увидеть результат вычислений, подобно тому, как работает блок условия if else в синхронном коде.
Конкурентная задача начинается, когда вы используете async let, создаете открепленную (detached) задачу или группу задач.
Задача возобновляются в точке приостановке (suspention point), обозначаемой await.
Однако не все задачи являются структурированными.
important
Структурированные задачи создаются с помощью async let и групп задач, а неструктурированные — используя Task и Task.detached.

Структурированные задачи живут до конца области видимости, подобно локальным переменным и автоматически отменяются при выходе из области видимости. Такой подход даёт явно понять как долго задача должна жить.
note
Старайтесь, по возможности, использовать SC, вместо неструктурированных задач (Unstructured).
О преимуществах SC вы можете прочитать ниже, а пока посмотрим на конкретный пример.
Представьте, что в нашем распоряжении кухня с несколькими шеф поварами, каждый из которых хочет приготовить суп. Приготовление супа состоит из нескольких шагов: нарезка ингридиентов, мариновка курицы, варка бульона и финальное приготовление. Некоторые задачи можно выполнить параллельно, а другие только в определенном порядке.

Посмотрим на нашу функцию приготовления makeSoup.

Возможно вы захотите явно добавить неструктурированные задачи Task { … } к вашим функциям и будете ожидать возвращения значения.
Это подразумевает, что задача будет выполняться конкуретно, но все же это не рекомендуемый способ использовать конкурентность в Swift.
Посмотрите на эту же функцию, но с использованием SC.

Поскольку мы знаем количество дочерних задач, мы можем использовать удобный синтаксис async let.
В этом случае, задачи формируют структурированные отношения с родительской задачей. Чуть позже вы узнаете почему это важно.
makeSoup вызывает несколько асинхронных функций, одна из которых нарезка ингридиентов chopIngridients(…), которая принимает список ингридиентов и использует taskGroup { … } для конкрурентной нарезки.

Вы ознакомились с задачей по приготовлению супа. Самое время посмотреть как выглядит дерево иерархии (Task tree) этой задачи.

Дочерние задачи нашей функции выделены цветными квадратами. Стрелки указывают на отношения между родительскими и дочерними задачами.
Функция makeSoup содержит 3 дочерние задачи:
- Нарезка ингридиентов
chopIngridients(🍍, 🥕, 🧅) - Мариновка курицы
marinate(🍗) - Варка бульона
boil broth
Функция chopIngridients(…) использует taskGroup { … } для создания дочерних задач для каждого ингридиента. Мы используем 3 ингридиента, поэтому будут созданы 3 дочерних задачи. Такие родительские и дочерние отношения формируют дерево задач (task tree).
Про отмену задач сказано в другой главе.