Dasync/AsyncEnumerable

AsyncEnumerable interface can return completed Task

ndrwrbgs opened this issue · 1 comments

return await GetAsyncEnumeratorAsync(cancellationToken);

Minor difference - but possibly relevant as I try to wrap the IAsyncEnumerator<T> to the ones exposed in System.Interactive. The line above is async and awaits the Task rather than just returning the Task returned to it from the IAsyncEnumerator<T> interface implementation. Instead, we can remove both async and await from this method, and allow it to return the Task.FromResult task (which is a minor performance gain also).

There is a minor minor functional difference, in that async wraps up in a continuation, whereas returning a Task directly does not. This means that exceptions thrown when doing

System.Threading.Tasks.Task Demo(){
	throw new Exception();
	return SomeOtherTask();
}

will be thrown immediately (at a var demoTask = Demo() rather than at the await demoTask). However...

async System.Threading.Tasks.Task Demo2() {
	throw new Exception();
	await SomeOtherTask();
}

Demo2 would execute var demoTask = Demo2() fine, but throw at await demoTask.

All that said, this code can only throw if AsyncEnumerable.ctor is passed a null argument, and the change in exception behavior would just make IAsyncEnumerable.GetAsyncEnumeratorAsync match the behavior of IAsyncEnumerable<T>.GetAsyncEnumeratorAsync, so it's probably not a breaking change, practically speaking.

@ndrwrbgs, thanks for looking into the source code.

The aforementioned line

return await GetAsyncEnumeratorAsync(cancellationToken);

cannot simply cast Task<IAsyncEnumerator<T>> to Task<IAsyncEnumerator> because there is no covariant ITask<out T> interface - that's why it uses async-await.

As for the second case, yes, I agree that Task.FromException is the proper way to return an error. However, as you said, it might happen only in the constructor when null is passed, so there is no real reported impact.