Return Task instead of Task<TResult> from TaskCompletionSource
Solution 1
I know it can work to return Task(of object)
Well, that won't do what you expect it to.
The trouble is that you're trying to return a task... and an async method automatically wraps the return value in another task. It's not clear why you're using an async method at all here, to be honest. Why not just write this:
public Task UploadFilesAsync(string fileAPath, string fileBPath)
{
return Task.WhenAll(uploadFileAAsync(fileAPath), uploadFileBAsync(fileBPath));
}
Does that not do what you want? You just want a task which completes when both of the "suboperations" have completed, right? That's exactly what Task.WhenAll
returns. Your method is still non-blocking - it won't wait until the operations have completed before it returns. It's just that you're using the fact that Task.WhenAll
is non-blocking to achieve that, instead of an async method.
EDIT: Note that if you wanted to do something else in that method as well, you could make it an async method without using TaskCompletionSource
yourself:
public async Task UploadFilesAsync(string fileAPath, string fileBPath)
{
// Upload the files
await Task.WhenAll(uploadFileAAsync(fileAPath), uploadFileBAsync(fileBPath));
await SomethingElseAsync();
MaybeDoSomethingCheap();
}
Note that even though the async method here returns Task
, you don't have a return value - the async/await machinery handles all of that for you, returning a task which will complete when MaybeDoSomethingCheap()
has finished, or fault if an exception is thrown.
Solution 2
As far as I know there is no direct way to return a Task
object when employing TaskCompletionSource<T>
.
Generally I prefer to return a Task<bool>
type object at these situations. But you are right that, returning a generic type object makes no sense if return values of the function is not usable.
But in fact you do not need to create a TaskCompletionSource
since you have an await
keyword inside the function. TaskCompletionSource
is generally used to convert an synchronous function to an asynchronous one. Since you are already calling an asynchronous function (and in fact this seems as the only functionality) you do not need to create a TaskCompletionSource
.
public async Task UploadFilesAsync(string fileAPath, string fileBPath)
{
return await Task.WhenAll(uploadFileAAsync(fileAPath), uploadFileBAsync(fileBPath));
}
Jonathan Harrison
Updated on June 05, 2022Comments
-
Jonathan Harrison almost 2 years
As I have seen in several coding examples, as well as, what I can understand from this SO question I should be able to return a non-generic Task from TaskCompletionSource
(i.e., Return Task and not Task<TResult> from the method UploadFilesAsync)
Yet the following code:
public async Task UploadFilesAsync(string fileAPath, string fileBPath) { var tcs = new TaskCompletionSource<Object>(); //logic to process files try { await Task.WhenAll(uploadFileAAsync(fileAPath), uploadFileBAsync(fileBPath)); tcs.TrySetResult(null); } catch (Exception e) { tcs.SetException(e); } finally { //logic to clean up files } return tcs.Task; }
Produces the following syntax error
'UploadFilesAsync(string, string)' is an async method that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task<T>'?
I am targeting .NET 4.5. I know it can work to return Task(of object) but that makes the API feel "dirty". Is it preferred practice to return Task(of object) or is it possible to return Task (non-generic as shown in the code)?
-
Jonathan Harrison almost 11 yearsI have to unzip files, run the upload, and then delete the unpacked files. I wanted the uploads to run async and that is why I wrapped them in taskcompletionsource... or I would have done as you suggested. That makes sense that async wraps the return in another Task (no wonder it was tough seeing a working example with async/await). Thanks!
-
Jonathan Harrison almost 11 yearsThanks for the response and what you have written makes sense. See my comment to Jon Skeet as to why I used the TaskCompletionSource.
-
Jon Skeet almost 11 years@JonathanHarrison: Just to check - do you understand why you can do as I suggested, and it's still async? I'll add another bit which may help...
-
Jonathan Harrison almost 11 yearsThanks so much for doing the edit. That concept didn't click initially... That's beautiful that async/await actually handles an async void method like that.
-
Jon Skeet almost 11 years@JonathanHarrison: Yes - basically you should avoid writing genuine
async void Foo
methods as far as possible; they should almost always returnTask
. I'm glad it's clicked now :)