1

Consider the following code:

static async Task ThrowException()
{
    throw new Exception("exception 1");
    Thread.Sleep(1000);
    await Task.Delay(1000);
    throw new Exception("exception 2");
}
static async Task Sleep()
{
    Console.WriteLine("begin sleep");
    Thread.Sleep(1000);
    Console.WriteLine("end sleep");
    Console.WriteLine("begin delay");
    await Task.Delay(1000);
    Console.WriteLine("end delay");
}
static async Task Main(string[] args)
{
    {
        Console.WriteLine("begin var task = Sleep();");
        var task = Sleep();
        Console.WriteLine("end var task = Sleep();");
        Console.WriteLine("begin await task;");
        await task;
        Console.WriteLine("end await task;");
    }
    {
        try
        {
            Console.WriteLine("begin var task = ThrowException();");
            var task = ThrowException();
            Console.WriteLine("end var task = ThrowException();");
            Console.WriteLine("begin await task;");
            await task;
            Console.WriteLine("end await task;");
        }
        catch(Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }
}

The result is following:

begin var task = Sleep();
begin sleep
end sleep
begin delay
end var task = Sleep();
begin await task;
end delay
end await task;
begin var task = ThrowException();
end var task = ThrowException();
begin await task;
exception 1

My question is that why exception 1 appears after "begin await task" task but "end sleep" appears before "begin await task"? I think it does not start a new thread before await Task.Delay(), it should happen in the same thread like Thread.Sleep(), so I expect the "exception 1" throw immediately before "end var task = ThrowException();"

Snowy
  • 59
  • 8
  • 1
    I honestly do not know why this confuses you? Why do you think it should be different and how? And why / how do you think both are related? – Fildor Aug 19 '21 at 09:59
  • @Fildor I think it does not start a new thread before `await Task.Delay`, it should happen in the same thread like `Thread.Sleep`, so I expect the exception 1 throw immediately before "end var task = ThrowException();" – Snowy Aug 19 '21 at 10:34
  • I think this is a very good question and must admit, I'm also a bit confused. – Steeeve Aug 19 '21 at 10:58
  • _"I think it does not start a new thread before await Task.Delay()"_ - no new Threads are started, here. See [There is no thread](https://blog.stephencleary.com/2013/11/there-is-no-thread.html). – Fildor Aug 19 '21 at 11:04
  • @Fildor in this case there will be a new thread. Started by `Task.Delay(1000)`. And everything else continues on this thread afterwards, not on the main thread. But it doesn't make a difference on the outcome. – Steeeve Aug 19 '21 at 11:28
  • @Steeeve Not exactly: See https://stackoverflow.com/a/47428221/982149. But as you say: It wouldn't matter anyway. – Fildor Aug 19 '21 at 11:37
  • @Fildor That's why a wrote: in this case. I'm debugging right now the console app ;) – Steeeve Aug 19 '21 at 11:43
  • @Snowy: I recommend reading my [async intro](https://blog.stephencleary.com/2012/02/async-and-await.html) – Stephen Cleary Aug 20 '21 at 02:08

4 Answers4

2

This code is

static async Task ThrowException()
{
    throw new Exception("exception 1");
    Thread.Sleep(1000);
    await Task.Delay(1000);
    throw new Exception("exception 2");
}

similar to

static Task ThrowException()
{
    Task.Run(() =>
        {
            throw new Exception("exception 1");
            Thread.Sleep(1000);
            await Task.Delay(1000);
            throw new Exception("exception 2");
        }
    )
}

The trick is the exception is swallowed by the task. Then the exception is stored in the task and when you ask the task's result, that throw the exception. And await return the task's result.

In your case :

try
{
    Console.WriteLine("begin var task = ThrowException();");
    // 1) Display : "begin var task = ThrowException();"
    var task = ThrowException();
    // 2) Starts a task that immediately ends with an exception
    Console.WriteLine("end var task = ThrowException();");
    // 3) Display : "end var task = ThrowException();"
    Console.WriteLine("begin await task;");
    // 4) Display : "begin await task;"
    await task;
    // 5) await return the task result,
    // in this case it's throw the exception stored in the task.
    // Go to in catch
    Console.WriteLine("end await task;");
    var task = ThrowException();
    await task;
}
catch(Exception e)
{
    Console.WriteLine(e.Message);
    //6) Display the exception message
}
vernou
  • 6,818
  • 5
  • 30
  • 58
1

According to Microsoft Documentation

The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes. When the asynchronous operation completes, the await operator returns the result of the operation,

*I wont dive deep into how multithreading works, I've tried to use an over-simplified model to make it easy to understand what is going on here. Under the hood it's a little bit more complicated.

In both cases var task = Sleep(); and var task = ThrowException();, instructions within those methods are executed when you assign the Task to the task variable. The difference is that Sleep() method does not have anything to return, so the await here does nothing. On the other hand, ThrowExeption() has something to return. Note that throwing an exeption or returning a value from a function are very similar actions. In this case, the exeption is thrown way before you access it with the await keyword.

It's like sayin: Hey Sleep() and ThrowExeption(), do your work, I'll get to you later when I'll need your results.

*Later:

  • Hey Sleep(), give me your results

  • I did what you said, I don't have anything to return to you.

  • Hey TrowExeption(), give me the results of your work

  • Alright! I've been waiting for you, here is the exeption that you might be interested in.

Dorin Baba
  • 1,578
  • 1
  • 11
  • 23
1

There are already answers here, but as I was also confused, just a few words (too long for a comment). There is big difference between:

static async Task ThrowException()
{
    throw new Exception("exception 1");
}

and

static Task ThrowException()
{
    throw new Exception("exception 1");
}

The first one returns a failed Task, here the exception will be thrown by awaiting the task result. The second one throws immediately an exception.

Steeeve
  • 824
  • 1
  • 6
  • 14
0

I think you are confusing the question. Because everything in your program is executing in sequencial order.

I think your confusion is that you think you are executing the task in a asyncronous way so you are expecting other order maybe.

If you want to execute asyncronously you should do this for example:

Task task = Task.StartNew(Sleep());

Detail your question better please.

hesolar
  • 543
  • 4
  • 23