Пользовательский ввод с тайм-аутом не работает, как ожидалось - PullRequest
6 голосов
/ 29 апреля 2020

Почему циклы l oop не работают, как раньше, после тайм-аута (больше не печатается пользовательский ввод)?

#!/usr/bin/env raku

loop {
    my $str;
    my $timeout = Promise.in( 5 ).then({
        $str = 'Timeout';
    });
    my $user = Promise.start({
        $str = prompt '>';
    });
    await Promise.anyof( $timeout, $user );
    if $str eq 'q' {
        last;
    }
    say "[$str]";
}

Ответы [ 2 ]

5 голосов
/ 02 мая 2020

Это потому, что вы говорите с неправильным вызовом prompt, который находится в обещании, закрытом для другой переменной $str. Второй и последующие вызовы к блоку prompt, ожидающие первого вызова до конца sh. Но $str для получения значения первого вызова выходит за рамки видимости, поэтому ничего не происходит.

Звучит очень странно, но вот эксперимент, который вы можете провести, чтобы помочь вашей интуиции, пока я анализирую это более полно: запустите скрипт, дождитесь тайм-аута, затем введите q дважды в быстрой последовательности. Сценарий выходит после второго. Почему?

В первом l oop мы объявляем переменную $str, которую я собираюсь назвать "$str number 1", и создаем Promise, закрывающий число $str 1 и звонит prompt. prompt присоединяется к STDIN и не возвращается, пока не увидит новую строку. По истечении времени ожидания этот вызов prompt не прерывается. Это все еще работает. Все еще жду Обещание, к которому оно прикреплено (назовем его $user обещание 1), все еще активно, хотя переменная $user вот-вот выйдет за пределы go.

На втором l oop мы объявите новую переменную $str ("$str number 2"), создайте Promise, закрывающий it , и снова вызовите prompt. Но другой вызов prompt все еще использует STDIN, поэтому новый вызов блокируется и ждет, пока STDIN станет доступным. Если вы наберете что-то сейчас, это будет видно по исходному вызову prompt, который был прикреплен к $user обещанию 1 и закрыт по $str номеру 1.

$str номер 1 обновлен когда prompt возвращается, но это не имеет значения, потому что ты перестал на него смотреть. Условие if $str eq 'q' будет проверять $str число 2, потому что это переменная, которая была объявлена ​​в текущем l oop.

Второй вызов prompt затем немедленно запрашивает ввод, и если вы наберете q до истечения времени ожидания, он обновит версию $str, которую он закрыл, $str номер 2. Поскольку это та, которую проверяет ваше условие, l oop завершается.

Каждый тайм-аут начинает новый prompt без завершения старого, что означает, что вводимые пользователем данные никогда не присоединяются к тому же $str, который вы проверяете. Даже если вы осмотрите исходную переменную, последующие вызовы prompt все равно будут происходить и будут продолжать запрашивать даже после того, как выполнение покинуло блок.

Поскольку prompt не имеет способа указать время ожидания и У Раку нет способа «убить» запланированное Promises, я не думаю, что вам удастся решить эту проблему с prompt.

3 голосов
/ 29 апреля 2020

Логическая проблема в этом коде заключается в том, что тайм-аут Promise сработает через 5 секунд, даже если кто-то что-то ввел в предыдущей итерации. И поэтому он будет устанавливать $str в кажущееся случайное время.

Существует простое решение: просто убедитесь, что вы не присваиваете $str в коде тайм-аута, если он уже был установлен:

$str //= 'Timeout';

Для этого примера это не имеет большого значения, но обычно вы не хотите, чтобы код выполнялся произвольно, поэтому было бы лучше деактивировать Promise. К сожалению, вы не можете сделать это с помощью интерфейса Promise. Но метод Promise.in на самом деле является оберткой для метода ThreadPoolScheduler.cue, который возвращает объект Cancellation (https://docs.raku.org/routine/cue).

...