Channel::recv
(PHP 8.6+, True Async 1.0)
public Channel::recv(?Completable $cancellationToken = null): mixedПолучает следующее значение из канала. Операция является блокирующей — текущая корутина приостанавливается, если в канале нет доступных значений.
Если канал закрыт и буфер пуст, выбрасывается ChannelException. Если канал закрыт, но в буфере остались значения, они будут возвращены.
ChannelException означает «канал закрыт» — но не только штатным close(). Тем же исключением сообщается о дедлоке (NO_PRODUCERS, NO_CONSUMERS, DEADLOCK) и об исчезновении канала (DISPOSED, SCOPE_DISPOSED). Отличать их нужно по свойству $e->reason (перечисление ChannelCloseReason).
Поэтому catch (ChannelException) { break; } опасен: он превращает дедлок в «поток штатно кончился», и корутина молча завершается с нулём обработанных значений. Проверяйте reason — либо используйте foreach, который сам завершается при штатном закрытии и пробрасывает всё остальное.
Параметры
cancellationToken : Токен отмены (Completable), позволяющий прервать ожидание по произвольному условию. null — ожидание без ограничений (по умолчанию). Когда токен завершается, операция прерывается и выбрасывается AsyncCancellation. Для ограничения по времени можно использовать Async\timeout().
Возвращаемые значения
Следующее значение из канала (mixed).
Ошибки
- Выбрасывает
Async\ChannelException, если канал закрыт и буфер пуст. - Выбрасывает
Async\AsyncCancellation, если токен отмены был завершён.
Примеры
Пример #1 Получение значений из канала
<?php
use Async\Channel;
$channel = new Channel(5);
spawn(function() use ($channel) {
for ($i = 1; $i <= 5; $i++) {
$channel->send($i);
}
$channel->close();
});
spawn(function() use ($channel) {
try {
while (true) {
$value = $channel->recv();
echo "Получено: $value\n";
}
} catch (\Async\ChannelException $e) {
// Дедлок и потеря канала приходят тем же исключением — их нельзя принимать
// за штатный конец потока, иначе ошибка исчезнет без следа.
if ($e->reason !== \Async\ChannelCloseReason::EXPLICIT) {
throw $e;
}
echo "Канал закрыт и пуст\n";
}
});То же самое, но без ручной проверки — foreach завершается сам при штатном закрытии, а дедлок и потерю канала пробрасывает наружу:
spawn(function() use ($channel) {
foreach ($channel as $value) {
echo "Получено: $value\n";
}
echo "Канал закрыт и пуст\n";
});Пример #2 Получение с таймаутом
<?php
use Async\Channel;
$channel = new Channel();
spawn(function() use ($channel) {
try {
$value = $channel->recv(Async\timeout(2000));
echo "Получено: $value\n";
} catch (\Async\AsyncCancellation) {
echo "Данные не поступили за 2 секунды\n";
}
});Пример #3 Получение с произвольным токеном отмены
<?php
use Async\Channel;
use Async\Future;
$channel = new Channel();
$cancel = new Future();
spawn(function() use ($channel, $cancel) {
try {
$value = $channel->recv($cancel);
echo "Получено: $value\n";
} catch (\Async\AsyncCancellation) {
echo "Получение отменено\n";
}
});
// Отменяем из другой корутины
spawn(function() use ($cancel) {
Async\delay(500);
$cancel->complete(null);
});См. также
- Channel::recvAsync — Неблокирующее получение
- Channel::send — Отправить значение в канал
- Channel::isEmpty — Проверить, пуст ли буфер
- Channel::getIterator — Итерация по каналу через foreach