Интерпретируйте результат отправки элемента в AsyncStream: означает ли успешный yield, что потребитель уже обработал значение?
Нет. Результат yield сообщает только о судьбе элемента относительно внутреннего состояния потока: элемент принят для доставки, вытеснен политикой буферизации или поток уже завершён. Даже enqueued не означает, что потребитель получил или обработал значение.
AsyncStream не предоставляет встроенного подтверждения обработки и обратного давления через yield: отправитель не приостанавливается в ожидании потребителя.
AsyncStream появился как адаптер между callback-ориентированными API и моделью async/await. Старый API обычно вызывает callback в произвольный момент, а современный код хочет последовательно получать значения через for await.
При таком переходе нужно было определить поведение, если производитель быстрее потребителя. Поэтому поток получил буферизацию и результат yield, позволяющий производителю узнать, был ли элемент принят, отброшен или поток уже завершён.
Предположим, датчик, сокет или callback API быстро публикует события, а обработчик выполняет медленные операции. Если считать успешный yield подтверждением обработки, можно преждевременно удалить данные, подтвердить внешнюю операцию или освободить ресурс, который ещё используется потребителем.
Кроме того, при ограниченном буфере часть значений может быть отброшена. Это означает, что AsyncStream подходит не для каждого протокола доставки: он может быть допустим для обновлений состояния, но опасен для обязательных команд или финансовых событий.
yield возвращает результат AsyncStream.Continuation.YieldResult:
enqueued — значение принято потоком: оно передано ожидающему итератору или помещено в буфер;dropped — значение не будет доставлено, обычно из-за политики буферизации, сохраняющей другие элементы;terminated — поток уже завершён, поэтому значение не будет доставлено.Для enqueued доступна информация об оставшейся вместимости буфера, но это не счётчик необработанных элементов и не гарантия будущей доставки конкретному обработчику. Потребитель может ещё не начать выполнение, завершиться с отменой или получить значение позже.
Вызов yield синхронный и сам по себе не ждёт завершения обработки. Если требуется подтверждение именно обработки, его нужно моделировать отдельно: например, передавать идентификатор операции и возвращать подтверждение через другой канал либо использовать последовательную очередь с явным протоколом acknowledgements.
Политика буферизации определяет компромисс. bufferingNewest сохраняет более новые значения и может терять старые, что удобно для состояния UI; bufferingOldest сохраняет старые значения и отбрасывает новые, что может быть полезно для ограничения нагрузки, но также приводит к потере данных. Ни одна из этих политик не создаёт гарантии доставки каждого элемента.
Здесь enqueued означает принятие значения структурой потока, а строка получено выполняется позже в потребителе. Между этими событиями нет гарантии, что обработка уже началась или завершилась.
Сервис получает события изменения положения устройства. UI должен показывать только самое свежее положение, а обработка каждого промежуточного значения не нужна. Вариант с неограниченным буфером не подходит: при медленном UI он накапливает устаревшие значения и увеличивает задержку.
Можно выбрать bufferingOldest, но тогда UI будет последовательно обрабатывать старые координаты. Можно использовать bufferingNewest(1): старые значения будут вытесняться, зато отображение быстрее приближается к текущему состоянию. Это решение оправдано, потому что координаты являются снимками состояния, а не независимыми обязательными событиями.
Если тот же механизм применить к платежным операциям, это будет ошибкой: потеря элемента недопустима, а enqueued не подтверждает выполнение платежа. Для такого сценария нужен протокол надёжной доставки и отдельное подтверждение завершения обработки.
Означает ли enqueued наличие свободного места в буфере?
Не обязательно в бытовом смысле. Результат означает, что значение принято потоком; оно могло быть немедленно передано ожидающему потребителю, а не сохранено в буфере. Информация о вместимости относится к состоянию буфера после операции, но не описывает прогресс обработки.
Можно ли по dropped определить, какой элемент был потерян?
Результат содержит отброшенное значение, поэтому его можно идентифицировать и залогировать. Однако это не превращает поток в надёжный транспорт: факт обнаружения потери не восстанавливает значение и не гарантирует, что другие элементы не будут потеряны позднее.
Создаёт ли yield обратное давление, если потребитель медленный?
Нет. yield не является async-операцией и не обязан ждать потребителя. Ограничение буфера влияет на результат отправки и может привести к вытеснению или отбрасыванию значений; если требуется замедлять производителя, обратное давление нужно реализовать отдельным протоколом или выбрать другой механизм обмена.