Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yielding in catch blocks not supported in async iterators #39583

Open
Daniel15 opened this issue Oct 30, 2019 · 4 comments

Comments

@Daniel15
Copy link

@Daniel15 Daniel15 commented Oct 30, 2019

Imagine something like:

public async IAsyncEnumerable<string> DoStuff()
{
	await foreach (var reply in CallSomeApi())
	{
		yield return "Message: " + reply;
	}
}

Where CallSomeApi is actually some streaming API that provides an IAsyncEnumerable (for example, .ResponseStream.ReadAllAsync() on a gRPC streaming response).

Say CallSomeApi can throw exceptions, for example:

private static async IAsyncEnumerable<string> CallSomeApi()
{
	yield return "foo";
	await Task.Delay(100);
	throw new Exception("Something bad happened");
}

How am I supposed to handle exceptions thrown by CallSomeApi?

My initial naive thought was something like this:

public async IAsyncEnumerable<string> DoStuff(
	[EnumeratorCancellation] CancellationToken cancellationToken
)
{
	try
	{
		await foreach (var reply in CallSomeApi().WithCancellation(cancellationToken))
		{
			yield return "Message: " + reply;
		}
	}
	catch (Exception ex)
	{
		yield return "Error: " + ex.Message;
	}
}

But this throws CS1626:

Error	CS1626	Cannot yield a value in the body of a try block with a catch clause

(jcouv edit): Relates to dotnet/csharplang#765

@stephentoub

This comment has been minimized.

Copy link
Member

@stephentoub stephentoub commented Oct 30, 2019

You can store the exception into a local and then outside of the catch block yield.

@stephentoub stephentoub transferred this issue from dotnet/corefx Oct 30, 2019
@stephentoub stephentoub changed the title Catching exceptions in IAsyncEnumerable Yielding in catch blocks not supported in async iterators Oct 30, 2019
@Daniel15

This comment has been minimized.

Copy link
Author

@Daniel15 Daniel15 commented Oct 30, 2019

I don't know if that fully solves it as I can't yield in the try block either.

@Daniel15

This comment has been minimized.

Copy link
Author

@Daniel15 Daniel15 commented Nov 1, 2019

For what it's worth, I ended up using channels (Channel.CreateUnbounded) rather than async streams, to work around this issue. My use case was for SignalR server-to-client streaming which supports both IAsyncEnumerable<T> and ChannelReader<T>. Closest equivalent to the example code in my original post is something like this:

public async Task DoStuff(ChannelWriter<string> writer)
{
	try
	{
		await foreach (var reply in CallSomeApi().WithCancellation(cancellationToken))
		{
			await writer.WriteAsync("Message: " + reply);
		}
	}
	catch (Exception ex)
	{
		await writer.WriteAsync("Error: " + ex.Message);
	}
}
@gafter

This comment has been minimized.

Copy link
Member

@gafter gafter commented Nov 6, 2019

The specification for async iterators does not call out this as being an error, so it is a bug (possibly in the specification, possibly in the compiler)

@gafter gafter added the Bug label Nov 6, 2019
@gafter gafter added this to the Compiler.Next milestone Nov 6, 2019
@jcouv jcouv self-assigned this Nov 13, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.