Friday 23 March 2018

Await for completed/resolved Task/Promise

This post is complementary to this one. I've come across another difference between how async/await works in .Net/Javascript, awaiting on an already completed/resolved Task/Promise.

In C#, if you do an await on a completed Task (for example you've created it already completed with Task.FromResult), the code will continue synchronously, there is not a "jump outside the method that will be continued later". Oversimplifying it, we could say that the ContinueWith method invocation that the compiler creates under the covers checks if the Task is already completed and hence decides to call the continuation delegate in sequence. Let's see an example, notice how while in DoAsync1 there is no "jumping out", everything runs synchronous, in DoAsync2 we have the "jump out and continue later" behaviour:


static async Task<string> DoAsync1(string st)
{
Console.WriteLine("DoAsync, before await");
//Task.FromResult returns an already completed Task, this gets immediatelly and there is not an interruption of the current method and later reentrance
var res = await Task.FromResult(st.ToUpper());
Console.WriteLine("DoAsync, after await");
return res;
}

static async Task<string> DoAsync2(string st)
{
Console.WriteLine("DoAsync2, before await");
await Task.Delay(500);
Console.WriteLine("DoAsync2, after await");
return st.ToUpper();
}

Console.WriteLine("started");
Task<string> task =  DoAsync1("hi");
Console.WriteLine("after calling doAsync1");
Console.WriteLine(task.Result);

Console.WriteLine("---------------");


Console.WriteLine("going on");
task =  DoAsync2("hi");
Console.WriteLine("after calling doAsync2");
Console.WriteLine(task.Result);

// started
// DoAsync, before await
// DoAsync, after await
// after calling doAsync1
// HI
// ------------------
// going on
// DoAsync2, before await
// after calling doAsync2
// DoAsync2, after await
// HI

In Javascript the behaviour is different, even if the Promise is already resolved (Promise.resolve("a")), the "jumping out of the function" takes place anyway. Let's see an example. The behaviour of test1 and test2 functions is the same, in both cases we have that the "after invoking" runs before than the "after await" :


async function test1(){
 console.log("test1 before await");
 let res = await Promise.resolve("hi");
 console.log("test1 after await");
}

async function test2(){
 console.log("test2 before await");
 let res = await new Promise((res, rej) => {
  setTimeout(
   () => res("hi")
   , 1000);
 });
  
 console.log("test2 after await");
}

test1();
console.log("after invoking test1");

test2();
console.log("after invoking test2");


//test1 before await
//after invoking test1
//test2 before await
//after invoking test2
//test1 after await
//test2 after await

No comments:

Post a Comment