0

I am trying to wade through the async functionality (C# .NET) and was curious whether an async function which returns a Task (i.e. promise) is always usable in an async function ONLY. Compiler and documentation indicate this of course, but I wanted to see if I am missing something which gets around this.

For example, the ReturnRandomIntegerAsync(i) returns a Task. Note that the while loop which runs every 5 seconds is faster than the function which takes 7 seconds :

        void MyFunction(){
           while(true)            
          {
            i++;
            var sw = Stopwatch.StartNew();

            Thread.Sleep(5000);                

            Task<int> j = ps.ReturnRandomIntegerAsync(i);// Call to an async 
            // method which has a sleep for 7 seconds (and awaits for sleep to be over))
            // **OUTPUT j**.

            sw.Stop();

            var timespan = sw.Elapsed;
            Console.WriteLine("Elapsed time : " + timespan.Seconds + "\n\n");// Time taken for this iteration. 
          }
        }

In this example, I cannot do an await on the ps.ReturnRandomIntegerAsync(i) unless I make the MyFunction() an async one. If I don't, then j is meaningless. If I do a .Result on the ps.ReturnRandomIntegerAsync(i), then it breaks async and the elapsed time (last line in while loop) shows 12 seconds (5 in the loop + 7 in the method).

If I want the value of j in every loop iteration ,the only solution I can think of in this scenario is to have j stored somewhere (array, collection etc.) by the async method and then retrieved later.

Would that be the correct approach ?

3
  • Task has been around for longer than async and await. Commented Mar 20, 2017 at 18:57
  • Question for you: here, I have a method Foo that returns IEnumerable<int>. Am I required to foreach that sequence as soon as it is returned? If yes, why? If no, what are some things I could do with an IEnumerable<int> other than foreaching it? (In case it is not clear: foreach extracts values from sequences. await extracts values from tasks. Logically they are the almost same thing, so if you can answer the question about sequences, you probably already know the answer about tasks.) Commented Mar 20, 2017 at 20:05
  • @Eric, I see your point. And I think that is what I alluded to in the second to last para in my own question. To answer your question - No, I don't really need to foreach as soon as I receive the IEnumerable<int>. I can always add that to another collection (list, stack) and process it later. In my real-world example, the async is awaiting a 200 OK server response and updates a log ..... which I think the async method itself should be doing. Thanks ! Commented Mar 20, 2017 at 20:33

2 Answers 2

2

No you don't need to use an async method to use a Task. The async keyword is simply a language feature that makes working with asynchronous operations more convenient. You can use the actual operations on Task itself (most notably ContinueWith) when writing a method and do it "the old way" if you want.

Alternatively some methods can be written entirely by composing other Task returning methods, and may not need to be async themselves.

Of course, it will indeed be quite common for methods you write that use Task instances to be async. Situations in which it's unnecessary aren't super common, and writing things out by hand, without using the language feature, is almost always way more work and doesn't have a suitable benefit.

Sign up to request clarification or add additional context in comments.

2 Comments

Ok, what I am hearing then is that instead of creating out an async method, I can do a Task.Run( <Method definition>) to avoid the await within MyMethod(). But if I want the result of the task in the example (j which is a random number) which takes longer than the loop, the only way would be that the Task that is run will handle the response and do whatever's necessary within Run() itself. I would have to do a Task.Wait if I want the answer within the while loop ..... which will basically halt the loop till Wait returns().
@user1554876 No, you should not be using Task.Run here at all. If you want the loop to continue after the asynchronous operation has finished, you should await it. You could use ContinueWIth to write the method without using the async keyword, but it would be a lot of work (to do correctly) for no real benefit.
2

You can certainly store tasks and await them later.

Task.WhenAll is a common use case for this:

var task1 = SomethingAsync();
var task2 = SomethingElseAsync();

// Both operations are "in flight" at this point.

// Asynchronously wait for them both to complete.
await Task.WhenAll(task1, task2);

Another example is for long-lived "main loop" kind of tasks. For example, a task that continually reads from a network stream. In this case, you want to eventually "join" with the task to be able to detect exceptions. My preferred method of doing this is to await the task in an async Task (or - very rarely - an async void) method, but you could also use ContinueWith to detect those exceptions.

1 Comment

That's still an example of using a Task returning method in an async method. The question asks about calling such a method in a method that's not actually marked as async.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.