Задачи с yield¶
В QTasks задача может возвращать не только одно значение через return, но и
несколько значений через yield.
Такая задача рассматривается как генератор: она последовательно выдаёт
промежуточные результаты, которые могут быть обработаны и преобразованы.
Пример: генераторная задача с generate_handler¶
async def yield_func(result: int) -> int:
print(result)
return result + 2
@app.task(
description="Тестовая задача с генератором.",
generate_handler=yield_func,
)
async def test_yield(n: int):
for _ in range(n):
n += 1
yield n
Вызов задачи:
task = await test_yield.add_task(5, timeout=50)
print(task.returning)
# Результат: [8, 9, 10, 11, 12]
Что здесь происходит:
- Функция
test_yield— асинхронный генератор: она делаетyield nвнутри цикла. - Для каждого значения
nвызываетсяyield_func(result), и именно результатyield_funcпопадает в итоговый список. - При
n = 5цикл выполняется 5 раз, выдавая значения6, 7, 8, 9, 10, аyield_funcпревращает их в8, 9, 10, 11, 12.
Итог: task.returning — это список значений, которые вернул generate_handler.
Как это работает "под капотом"¶
Ниже — упрощённое описание внутренней логики без лишних деталей.
- Определение, что задача — генератор
При объявлении
@app.taskдля функции создаётся внутренняя модель задачиTaskExecSchema. В ней флагgeneratingустанавливается вTrue, если функция является генератором:
inspect.isasyncgenfunction(func) or inspect.isgeneratorfunction(func)
-
Выбор пути выполнения в TaskExecutor Когда
(A)syncTaskExecutorначинает выполнять задачу (execute()), он проверяетgenerating: -
если
generating == True→ вызывается внутренняя функцияrun_task_gen(); -
иначе → используется обычный путь
run_task(). -
Итерация по значениям генератора Внутри
run_task_gen()задача выполняется как генератор: -
для
async defиспользуетсяasync for result in func(...); -
для обычной
def— циклwhile Trueс обработкойStopIteration. -
Обработка через
generate_handlerНа каждыйresult, полученный изyield, вызываетсяgenerate_handler(result)(если он задан). Возвращаемые значенияgenerate_handlerсобираются в список. -
Финальный результат задачи По умолчанию
(A)syncTaskExecutorвозвращает список всех результатов, полученных из генератора: -
либо непосредственно значений
yield, - либо значений, преобразованных через
generate_handler.
Важно: и TaskExecSchema, и (A)syncTaskExecutor могут быть заменены
пользователем — в том числе на этапе @app.task(executor=NewTaskExecutor), если
требуется другой способ обработки генераторов.
Когда использовать задачи с yield¶
Задачи-генераторы удобны, когда нужно:
- получать поток результатов вместо одного значения;
- поэтапно обрабатывать данные (например, чанки файлов, батчи записей);
- логировать или агрегировать промежуточные результаты через
generate_handler; - строить собственные мини-пайплайны внутри одной задачи.
В остальных случаях достаточно обычной задачи с return.