Is this the effect you are trying to get?

using System;
using System.Threading.Tasks;
namespace asyncConsoleTyping
{
class Program
{
private static Random _rnd = new Random();
public static void Main(string[] args)
{
AsyncMain().Wait();
}
public static async Task AsyncMain()
{
await Type("This is a test");
await Type("This is another test");
await Type("What is your name?");
var name = Console.ReadLine();
await Type($"Hello {name}!");
Console.Read();
}
public static async Task Type(string text)
{
var prevColor = Console.ForegroundColor;
Console.ForegroundColor=ConsoleColor.Green;
foreach (char c in text)
{
Console.Write(c);
await Task.Delay(10 + _rnd.Next(30));
}
Console.WriteLine();
Console.ForegroundColor = prevColor;
}
}
}
This works by asynchronously printing strings a character at a time, with a small random delay between each one. It also changes the text color while printing, and restoring it afterwards. You can call it with await Type(SomeString)
, and the console won't lock up while printing characters. Execution will resume on the next line after the await
once the entire string
has been printed. It doesn't actually use a Timer
, as there are more appropriate Task
based mechanisms for doing this (Task.Delay
).
You will have to use a small "trick" to get a console application to use async
await
properly, since Main
can't be a Task
. Just create an AsyncMain
function, as in my example, and call it from Main
with AsyncMain().Wait()
. Once C# 7.1 is released, you won't need to do this, as public static Task Main(string[] args);
(among others) will be considered a valid entry point.
There are a few other "gotchas" for async console applications that you can read about here.
You can cancel a call to an async
Task
by using CancellationTokenSource
, and the CancellationToken
it creates.
Any Task
that can be canceled should accept a CancellationToken
as an argument. That Token is created by calling the Token
function on a CancellationTokenSource
. When the source is created, you can specify a timeout value (var cts = new CancellationTokenSource(1000);
), or you can call Cancel
on it (cts.Cancel
) from anywhere that has a reference to the token. Your async
method can then pass that token on to anything that it awaits (that supports cancellation). It should also periodically check IsCancellationRequested
to see if it should cancel its work.
This is a version of the above example that supports cancellation:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace asyncConsoleTyping
{
class Program
{
private static Random _rnd = new Random();
public static void Main(string[] args)
{
AsyncMain().Wait();
}
public static async Task AsyncMain()
{
await Type("This is a test");
await Type("This is another test");
await Type("What is your name?");
var name = Console.ReadLine();
await Type($"Hello {name}!");
var cts = new CancellationTokenSource(1000); //Auto-cancels after 1 second
try
{
await Type("This String can get canceled via a CancellationToken", cts.Token);
}
catch (Exception ex)
{
Console.ForegroundColor=ConsoleColor.Red;
Console.WriteLine($"Canceled: {ex.Message}");
}
}
public static Task Type(string text)
{
return Type(text, CancellationToken.None); //This overload doesn't support cancellation, but it calls the one that does. Passing in CancellationToken.None allows it to work without checking to see if a "real" token was passed.
}
public static async Task Type(string text, CancellationToken ct)
{
var prevColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Green;
foreach (char c in text)
{
Console.Write(c);
await Task.Delay(10 + _rnd.Next(30), ct); //Pass the Cancellationtoken in to Task.Delay so the delay can be canceled
if (ct.IsCancellationRequested) //Check to see if the task was canceled, if so, exit the loop through the characters.
{
break;
}
}
Console.WriteLine();
Console.ForegroundColor = prevColor;
}
}
}
The example above that shows cancellation actually has a subtle bug in it that could cause the console color to not get reset. await Task.Delay(10 + _rnd.Next(30), ct);
will throw an exception on cancel, and not go in to the color reset code. That can be fixed by changing it to this:
public static async Task Type(string text, CancellationToken ct)
{
var prevColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Green;
try
{
foreach (char c in text)
{
Console.Write(c);
await Task.Delay(10 + _rnd.Next(30), ct);
}
}
finally
{
Console.WriteLine();
Console.ForegroundColor = prevColor;
}
ct.ThrowIfCancellationRequested(); //Omit this line if you don't want an exception thrown on cancellation (but be aware of the consequences!)
}