Как вырваться из IAsyncEnumerable при итерации? - PullRequest
0 голосов
/ 23 декабря 2018

C # 8 добавляет поддержку асинхронных блоков итераторов, так что вы можете ожидать и возвращать IAsyncEnumarator вместо IEnumerable:

public async IAsyncEnumerable<int> EnumerateAsync() {
    for (int i = 0; i < 10; i++) {
        yield return i;
        await Task.Delay(1000);
    }
}

с неблокирующим код, который выглядит следующим образом:

await foreach (var item in EnumerateAsync()) {
    Console.WriteLine(item);
}

Это приведет к тому, что мой код будет работать в течение 10 секунд.Однако иногда я хочу вырваться из await foreach до того, как все элементы будут использованы.Однако с break нам нужно будет дождаться окончания текущего ожидаемого Task.Delay. Как мы можем немедленно выйти из этого цикла, не ожидая каких-либо висячих асинхронных задач?

1 Ответ

0 голосов
/ 23 декабря 2018

Использование CancellationToken является решением, поскольку это единственное, что может отменить Task.Delay в вашем коде.То, как мы получаем его в вашем IAsyncEnumerable, это передаем его в качестве параметра при его создании, поэтому давайте сделаем это:

public async IAsyncEnumerable<int> EnumerateAsync(CancellationToken cancellationToken = default) {
    for (int i = 0; i < 10; i++) {
        yield return i;
        await Task.Delay(1000, cancellationToken);
    }
}

С потребляющей стороной:

// In this example the cancellation token will be caneled after 2.5 seconds
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2.5));
await foreach (var item in EnumerateAsync(cts.Token)) {
    Console.WriteLine(item);
}

Конечно, это отменит перечисление после того, как были возвращены 3 элемента, но закончится TaskCanceledException, выброшенным из Task.Delay.Чтобы изящно выйти из await foreach, мы должны поймать его и сломать на стороне производства:

public async IAsyncEnumerable<int> EnumerateAsync(CancellationToken cancellationToken = default) {
    for (int i = 0; i < 10; i++) {
        yield return i;
        try {
            await Task.Delay(1000, cancellationToken);
        } catch (TaskCanceledException) {
            yield break;
        }
    }
}

Примечание

На данный момент это все еще в предварительном просмотре и может быть изменено.Если вы заинтересованы в этой теме, вы можете посмотреть обсуждение языковой команды C # о CancellationToken в IAsyncEnumeration.

...