- changed status to on hold
Generator API
Здравствуйте.
Сейчас интерфейс ev
построен на колбеках, это не очень удобно, когда работаешь с генераторами, нельзя передать как колбек [$generator, 'send']
потому что метод send
может принимать только один аргумент.
Я предлагаю, разрешить использовать в качестве колбека объект Generator и вызывать его метод send($data) это будет очень удобно, это возможно сделать?
Спасибо.
Comments (12)
-
repo owner -
У вас отличный пример который показывает сколько лишнего кода нужно писать чтобы использовать генераторы.
В примере создаются два объекта
Closure
(замыкание) которые здесь нужны только как обвертка для работы с объектамиGenerator
.Замыкание, здесь не нужно, это лишний ненужный код, который кстати добавляет ещё одно звено в стек вызовов, потребляют память.
Вот код, без замыканий и со всеми возможностями по управлению объектами
EvWatcher
<?php class Test { public $idle; public $timer; public function gen() { for ($i = 0; (yield $i++) < 10;); } public function idle($gen) { $data = yield; if (!$gen->valid()) { $this->idle->stop(); return; } echo $gen->current(), PHP_EOL; sleep(1); $gen->next(); } } $obj = new Test; $gen = $obj->gen(); $obj->timer = new EvTimer(3, 0, $gen); $obj->idle = new EvIdle($obj->idle($gen)); Ev::run();
Проблема не только в том что замыкания лишние звено, мы сейчас не можем в
callable
хендлерах использовать операторyield
потому что это уже будет не замыкания а генератор, с которым напрямую работатьEv
пока ещё не умеет, но надеюсь это можно сделать. -
repo owner Я всё равно не понимаю идеи.
Колбеки по определению являются обработчиками событий. Генераторы по определению являются источниками данных. Генераторы не являются представителями типа callable.
Я не вижу проблемы в том, что при необходимости получить очередную порцию данных в ответ на событие, мы делаем это в колбеке, дёргая генератор. Лишь в одном редком случае мы можем попытаться увидеть в этой схеме проблему - когда единственной функцией колбека является получение очередной порции данных из генератора, причём без сохранения этих данных где-либо, кроме как в самом генераторе, - это разве что оверхед вызова функции. Я не измерял, но сомневаюсь, что оверхед вызова функции больше оверхеда вызова
Generator::next()
.Кроме того, в случае
$obj->timer = new EvTimer(3, 0, $gen);
мы таки не имеем возможности управлять объектом таймера в момент обработки события "прошло 3 секунды". Конечно, мы могли бы управлять таймером откуда угодно, воспользовавшись областью видимости класса илиuse
. Но момент, ближайший к возникновению события, заблокирован ограниченным характером генератора.Таким образом, я пока не вижу необходимости в поддержке такой функции, извините.
-
Я всё равно не понимаю идеи.
Идея простая, мы используем генераторы для реализации
async/await
API.Колбеки по определению являются обработчиками событий
Да, в таких языках как Python, Node.js, обработчиками событий могут быть
async function
которые по сути являются генераторами (корутинами). В РНР пока что дляasync function
приходится использовать генераторы, ещё нет синтекс сахараasync/await
.Кроме того, в случае $obj->timer = new EvTimer(3, 0, $gen); мы таки не имеем возможности управлять объектом таймера в момент обработки события
Возможность эта есть.
<?php class Test { public $idle; public $timer; public function gen() { for ($i = 0; (yield $i++) < 10;); $this->timer->stop(); } }
Генераторы даже более удобны для замыкания внешних переменных, вот простой пример:
<?php $a = 'a'; $b = 'b'; $c = 'c'; $callable = function() use($a, $b, $c) { ... }; $gen = (function($a, $b, $c) { yield })($a, $b, $c);
В конструктор генератора отправляются
($a, $b, $c)
, с замыканием нужно использоватьuse($a, $b, $c)
Таким образом, я пока не вижу необходимости в поддержке такой функции, извините.
Очень жаль, это означает что мы по прежнему должны оборачивать генераторы в замыкания, чтобы использовать генераторы для
async/await
функций. -
repo owner Добавил поддержку
<=2
аргументов в ветке https://bitbucket.org/osmanov/pecl-ev/branch/issue27 . Соответственно, в генераторе получим объектEvWatcher
. Попробуйте. Если всё OK, выложу в PECL. -
Ок, попробую, если я правильно понял, в этой ветке можно будет использовать
[$gen, 'send']
? -
repo owner Ок, попробую, если я правильно понял, в этой ветке можно будет использовать [$gen, 'send']?
Да:
<?php $gen = (function () { for (;;) var_dump((yield)); })(); $timer = new EvTimer(0, 1, [$gen, 'send'], 'user data'); Ev::run();
object(EvTimer)#3 (6) { ["repeat"]=> float(1) ["remaining"]=> float(0.99890625599801) ["is_active"]=> bool(true) ["data"]=> string(9) "user data" ["is_pending"]=> bool(false) ["priority"]=> int(0) } ...
-
Пытаюсь проверить новую версию из ветки
issue27
Но все равно выдает ошибкуPHP Warning: Generator::send() expects exactly 1 parameter, 2 given in ...
Возможно это у меня кривые руки, собираю модуль так:
cd /opt git clone https://ua-san@bitbucket.org/osmanov/pecl-ev/branch/issue27.git cd issue27 phpize ./configure make && make install cp /opt/issue27/modules/ev.so /usr/lib64/php/modules/ev.so
Я правильно все делаю? :)
-
repo owner Наверно это Mac, а на Mac может быть всё. Поэтому вот точные команды, которые в Bash должны работать.
git clone https://bitbucket.org/osmanov/pecl-ev.git cd pecl-ev git checkout issue27 phpize ./configure make ( cat <<'EOS' <?php $gen = (function () { for (;;) var_dump((yield)); })(); $timer = new EvTimer(0, 1, [$gen, 'send'], 'user data'); Ev::run(); EOS ) >gen.php php -n -d extension=ev.so -dextension_dir=./.libs gen.php
-
Отлично, работает!
Прогнал в бенчмарке, вызов метода как
callable [$gen, 'send']
в 2 раза медленней, относительно прямого вызова$gen->send()
это не проблемаEv
это в ядре РНР.В любом случаи спасибо. Надеюсь в будущем в РНР появятся нативные
async
функции которые можно использовать как обработчики событий. -
В РНР 7.1 есть возможность ускорить вызов
Closure::fromCallable([$gen, 'send'])
-
repo owner - changed status to resolved
Fix: issue
#27where callbacks with < 2 arguments were rejected→ <<cset f3ed3dd386c6>>
- Log in to comment
Здравствуйте.
А в каких случаях может быть необходимо передавать
Generator::send
в качестве колбека? Например, в следующем скрипте мы без проблем пользуемся API генераторов:Кроме того, в своём колбеке мы имеем возможность управлять объектом
EvWatcher
. В случае же методаGenerator::send
мы бы лишь слепо отправляли в генератор... данные, прикрепленные к объектуEvWatcher
?