Asynchronous Application
Patterns in C#
Frank A. Krueger
@praeclarum
Async
How C# Makes Every Other Language Look Stupid
Frank A. Krueger
@praeclarum
Why Async?
Why Await?
Tour of System.Threading.Tasks
with Real-world Uses of Await
Fak-world Uses of Await
Why Async?
Why Await?
Tour of System.Threading.Tasks
with Real-world Uses of Await
Fak-world Uses of Await
Apps are Async
• Interacting with the user
• Performing computations
• Downloading resources from the web
• Uploading actions and messages to services
• Reading and writing local storage
• Watching for motion changes, camera or
microphone events, network connectivity,
etc.
http://xamarin.com/evolve/2013
http://xamarin.com/evolve/2013
You can’t do anything!
http://calca.io
And So Async...
• .NET Events
• .NET 1.0 Async (IAsyncResult)
And So Async...
• .NET Events
• .NET 1.0 Async (IAsyncResult)
• Closures! Continuation Passing Style FTW!
And So Async...
• .NET Events
• .NET 1.0 Async (IAsyncResult)
• Closures! Continuation Passing Style FTW!
• IObservable<T>
And So Async...
• .NET Events
• .NET 1.0 Async (IAsyncResult)
• Closures! Continuation Passing Style FTW!
• IObservable<T>
ARVIS
And So Async...
• .NET Events
• .NET 1.0 Async (IAsyncResult)
• Closures! Continuation Passing Style FTW!
• IObservable<T>
Asynchronous Retrieval of
Values from Infinite Streams
And So Async...
• .NET Events
• .NET 1.0 Async (IAsyncResult)
• Closures! Continuation Passing Style FTW!
• IObservable<T>
• iOS Delegates
And So Async...
• .NET Events
• .NET 1.0 Async (IAsyncResult)
• Closures! Continuation Passing Style FTW!
• IObservable<T>
• iOS Delegates
• iOS Callbacks (Obj-C bocks)
And So Async...
• .NET Events
• .NET 1.0 Async (IAsyncResult)
• Closures! Continuation Passing Style FTW!
• IObservable<T>
• iOS Delegates
• iOS Callbacks (Obj-C bocks)
• Android Listeners, Event Interfaces
Why Async?
Why Await?
Tour of System.Threading.Tasks
with Real-world Uses of Await
Fak-world Uses of Await
hugMeanPeopleButton.TouchUpInside += delegate {
// Network IO
var tweets = twitterClient.GetMentions ();
// Computation
var q = from t tweets
where t.IsMean
select t;
// Lots of Network IO
foreach (var enemy in q) {
twitterClient.Reply (enemy, “Here’s a hug”);
}
// UI
statusLabel.Text =
string.Join (“, “, q.Select (x => x.Name)) +
“ have been hugged”;
};
©®™ 2013 Krueger Systems, Inc. All Rights Reserved.
hugMeanPeopleButton.TouchUpInside += delegate {
// Network IO
var tweets = twitterClient.GetMentions ();
// Computation
var q = from t tweets
where t.IsMean
select t;
// Lots of Network IO
foreach (var enemy in q) {
twitterClient.Reply (enemy, “Here’s a hug”);
}
// UI
statusLabel.Text =
string.Join (“, “, q.Select (x => x.Name)) +
“ have been hugged”;
};
Slow
Slow x N
Slow?
hugMeanPeopleButton.TouchUpInside += delegate {
// Network IO
twitterClient.GetMentions (
(tweets, tweetsError) => {
if (tweetsError != null)
Umm (tweetsError);
// Computation
var q = from t in tweets
where t.IsMean
select t;
// This is a loop, believe it or not
tweetsToHug = q.ToList ();
nextTweetToHug = 0;
HugTweet ();
});
}
List<Tweet> tweetsToHug = null;
int nextTweetToHug = 0;
void HugTweet ()
{
if (tweetsToHug == null) return;
// Lots of Network IO
if (nextTweetToHug < tweetsToHug.Count) {
var t = tweetsToHug [nextTweetToHug++];
twitterClient.Reply (
t,
“Here’s a hug”,
replyError => {
if (replyError != null)
Umm (replyError);
HugTweet ();
});
return;
}
// UI
statusLabel.Text =
string.Join (“, “,tweetsToHug.Select(x=>x.Name))
+ “ have been hugged”;
tweetsToHug = null;
}
Continuation Passing Style
(CPS)
hugMeanPeopleButton.TouchUpInside += delegate {
// Network IO
twitterClient.GetMentions (
(tweets, tweetsError) => {
if (tweetsError != null)
Umm (tweetsError);
// Computation
var q = from t in tweets
where t.IsMean
select t;
// This is a loop, believe it or not
tweetsToHug = q.ToList ();
nextTweetToHug = 0;
HugTweet ();
});
}
List<Tweet> tweetsToHug = null;
int nextTweetToHug = 0;
void HugTweet ()
{
if (tweetsToHug == null) return;
// Lots of Network IO
if (nextTweetToHug < tweetsToHug.Count) {
var t = tweetsToHug [nextTweetToHug++];
twitterClient.Reply (
t,
“Here’s a hug”,
replyError => {
if (replyError != null)
Umm (replyError);
HugTweet ();
});
return;
}
// UI
statusLabel.Text =
string.Join (“, “,tweetsToHug.Select(x=>x.Name))
+ “ have been hugged”;
tweetsToHug = null;
}
2 Functions?
hugMeanPeopleButton.TouchUpInside += delegate {
// Network IO
twitterClient.GetMentions (
(tweets, tweetsError) => {
if (tweetsError != null)
Umm (tweetsError);
// Computation
var q = from t in tweets
where t.IsMean
select t;
// This is a loop, believe it or not
tweetsToHug = q.ToList ();
nextTweetToHug = 0;
HugTweet ();
});
}
List<Tweet> tweetsToHug = null;
int nextTweetToHug = 0;
void HugTweet ()
{
if (tweetsToHug == null) return;
// Lots of Network IO
if (nextTweetToHug < tweetsToHug.Count) {
var t = tweetsToHug [nextTweetToHug++];
twitterClient.Reply (
t,
“Here’s a hug”,
replyError => {
if (replyError != null)
Umm (replyError);
HugTweet ();
});
return;
}
// UI
statusLabel.Text =
string.Join (“, “,tweetsToHug.Select(x=>x.Name))
+ “ have been hugged”;
tweetsToHug = null;
}
StateVariables?
hugMeanPeopleButton.TouchUpInside += delegate {
// Network IO
twitterClient.GetMentions (
(tweets, tweetsError) => {
if (tweetsError != null)
Umm (tweetsError);
// Computation
var q = from t in tweets
where t.IsMean
select t;
// This is a loop, believe it or not
tweetsToHug = q.ToList ();
nextTweetToHug = 0;
HugTweet ();
});
}
List<Tweet> tweetsToHug = null;
int nextTweetToHug = 0;
void HugTweet ()
{
if (tweetsToHug == null) return;
// Lots of Network IO
if (nextTweetToHug < tweetsToHug.Count) {
var t = tweetsToHug [nextTweetToHug++];
twitterClient.Reply (
t,
“Here’s a hug”,
replyError => {
if (replyError != null)
Umm (replyError);
HugTweet ();
});
return;
}
// UI
statusLabel.Text =
string.Join (“, “,tweetsToHug.Select(x=>x.Name))
+ “ have been hugged”;
tweetsToHug = null;
}
Subtle but Critical
Assignments?
hugMeanPeopleButton.TouchUpInside += delegate {
// Network IO
twitterClient.GetMentions (
(tweets, tweetsError) => {
if (tweetsError != null)
Umm (tweetsError);
// Computation
var q = from t in tweets
where t.IsMean
select t;
// This is a loop, believe it or not
tweetsToHug = q.ToList ();
nextTweetToHug = 0;
HugTweet ();
});
}
List<Tweet> tweetsToHug = null;
int nextTweetToHug = 0;
void HugTweet ()
{
if (tweetsToHug == null) return;
// Lots of Network IO
if (nextTweetToHug < tweetsToHug.Count) {
var t = tweetsToHug [nextTweetToHug++];
twitterClient.Reply (
t,
“Here’s a hug”,
replyError => {
if (replyError != null)
Umm (replyError);
HugTweet ();
});
return;
}
// UI
statusLabel.Text =
string.Join (“, “,tweetsToHug.Select(x=>x.Name))
+ “ have been hugged”;
tweetsToHug = null;
}
Recursion?
hugMeanPeopleButton.TouchUpInside += delegate {
// Network IO
twitterClient.GetMentions (
(tweets, tweetsError) => {
if (tweetsError != null)
Umm (tweetsError);
// Computation
var q = from t in tweets
where t.IsMean
select t;
// This is a loop, believe it or not
tweetsToHug = q.ToList ();
nextTweetToHug = 0;
HugTweet ();
});
}
List<Tweet> tweetsToHug = null;
int nextTweetToHug = 0;
void HugTweet ()
{
if (tweetsToHug == null) return;
// Lots of Network IO
if (nextTweetToHug < tweetsToHug.Count) {
var t = tweetsToHug [nextTweetToHug++];
twitterClient.Reply (
t,
“Here’s a hug”,
replyError => {
if (replyError != null)
Umm (replyError);
HugTweet ();
});
return;
}
// UI
statusLabel.Text =
string.Join (“, “,tweetsToHug.Select(x=>x.Name))
+ “ have been hugged”;
tweetsToHug = null;
}
Tail End of Process in a
Strange Function?
hugMeanPeopleButton.TouchUpInside += delegate {
// Network IO
twitterClient.GetMentions (
(tweets, tweetsError) => {
if (tweetsError != null)
Umm (tweetsError);
// Computation
var q = from t in tweets
where t.IsMean
select t;
// This is a loop, believe it or not
tweetsToHug = q.ToList ();
nextTweetToHug = 0;
HugTweet ();
});
}
List<Tweet> tweetsToHug = null;
int nextTweetToHug = 0;
void HugTweet ()
{
if (tweetsToHug == null) return;
// Lots of Network IO
if (nextTweetToHug < tweetsToHug.Count) {
var t = tweetsToHug [nextTweetToHug++];
twitterClient.Reply (
t,
“Here’s a hug”,
replyError => {
if (replyError != null)
Umm (replyError);
HugTweet ();
});
return;
}
// UI
statusLabel.Text =
string.Join (“, “,tweetsToHug.Select(x=>x.Name))
+ “ have been hugged”;
tweetsToHug = null;
}
Umm...
Our Languages
Were Not Designed for
Asynchronous Procedures
http://xamarin.com/evolve/2013
http://channel9.msdn.com/Events/Build/BUILD2011/TOOL-816T
Oh, that Anders...
hugMeanPeopleButton.TouchUpInside += delegate {
// Network IO
var tweets = twitterClient.GetMentions ();
// Computation
var q = from t tweets
where t.IsMean
select t;
// Lots of Network IO
foreach (var enemy in q) {
twitterClient.Reply (t, “Here’s a hug”);
}
// UI
statusLabel.Text =
string.Join (“, “, q.Select (x => x.Name)) +
“ have been hugged”;
};
hugMeanPeopleButton.TouchUpInside += async delegate {
// Network IO
var tweets = await twitterClient.GetMentionsAsync ();
// Computation
var q = from t tweets
where t.IsMean
select t;
// Lots of Network IO
foreach (var enemy in q) {
await twitterClient.ReplyAsync (t, “Here’s a hug”);
}
// UI
statusLabel.Text =
string.Join (“, “, q.Select (x => x.Name)) +
“ have been hugged”;
};
hugMeanPeopleButton.TouchUpInside += async delegate {
// Network IO
var tweets = await twitterClient.GetMentionsAsync ();
// Computation
var q = from t tweets
where t.IsMean
select t;
// Lots of Network IO
foreach (var enemy in q) {
await twitterClient.ReplyAsync (t, “Here’s a hug”);
}
// UI
statusLabel.Text =
string.Join (“, “, q.Select (x => x.Name)) +
“ have been hugged”;
};
hugMeanPeopleButton.TouchUpInside += async delegate {
// Network IO
var tweets = await twitterClient.GetMentionsAsync ();
// Computation, in the Background, dood
var q = await Task.Run (() =>
from t tweets
where t.IsMean
select t);
// Lots of Network IO
foreach (var enemy in q) {
await twitterClient.ReplyAsync (t, “Here’s a hug”);
}
// UI
statusLabel.Text =
string.Join (“, “, q.Select (x => x.Name)) +
“ have been hugged”;
};
C#
is Designed for
Asynchronous Procedures
Why Async?
Why Await?
Tour of System.Threading.Tasks
with Real-world Uses of Await
Fak-world Uses of Await
Task
TaskCompletionSource
TaskScheduler
CancellationToken
System.Threading.Task
Parallel Process
await task; // Async
task.Wait (); // Synchronous
System.Threading.Task
Fallible Parallel Process
try {
await task; // Async
task.Wait (); // Synchronous
}
catch (Exception) {
}
System.Threading.Task<T>
Parallel Process
That Returns aValue
var value = await task; // Async
task.Wait (); // Synchronous
var value = task.Result;
Where do Tasks come from?
var tweets = await client.GetTweetsAsync ();
await client.PostAsync (“Hello, World!”);
Call an Async Method
var tweets = await client.GetTweetsAsync ();
await client.PostAsync (“Hello, World!”);
Call an Async Method
Network? CPU? UI?
var value = await Task.Run (() => {
return 42;
});
await Task.Run (() => {
LaunchMissiles ();
});
Start a Background Task
Using a Function
mesh = sphere.Tesselate (opts);
mesh = await Task.Run (() =>
sphere.Tesselate (opts));
async Task<int> GetPopularityAsync ()
{
var followers = await
twitter.GetFollowersAsync ();
return followers.Sum (
x => x.FollowerCount);
}
Methods That Use await
Return Tasks
var value = await Task.Factory.FromAsync (
foo.BeginDoingSomething,
foo.EndDoingSomething,
null);
From .NET 1.0 Async
await Task.Factory.ContinueWhenAll (
downloadTasks,
ts => {});
Combinators
await Task.Factory.ContinueWhenAny (
actionStartGestures,
t => {});
await Task.Factory.ContinueWhenAll (
downloadTasks,
ts => {});
Combinators
await Task.Factory.ContinueWhenAny (
actionStartGestures,
t => {});
Take Multiple Tasks and turn them
into One Task
async Task UpdatePodcasts ()
{
ShowUpdatingUI ();
foreach (var feed in feeds) {
await feed.Update ();
}
ShowUpdatingCompletedUI ();
}
Concurrency?
async Task UpdatePodcasts ()
{
ShowUpdatingUI ();
foreach (var feed in feeds) {
await feed.Update ();
}
ShowUpdatingCompletedUI ();
}
Concurrency?
Sequential
async Task UpdatePodcasts ()
{
ShowUpdatingUI ();
Task.Factory.ContinueWhenAll (
feeds.Select (x => x.Update ()),
null);
ShowUpdatingCompletedUI ();
}
Concurrency!
Parallel
Task
TaskCompletionSource
TaskScheduler
CancellationToken
TaskCompletionSource<T>
Create tasks
whose execution and
termination you control
TaskCompletionSource<T>
Great way to interact with
ancient, old, fuddy duddy,
outdated async models
TaskCompletionSource<T>
tcs.Task
tcs.SetResult ()
tcs.SetException ()
tcs.SetCanceled ()
Task<int> GetAnswerAsync ()
{
var tcs = new TaskCompletionSource<int> ();
ThreadPool.QueueUserWorkItem (() => {
BuildAComputer ();
LetItRun ();
tcs.SetResult (42); // Terminate
});
return tcs.Task;
}
Task<int> GetAnswerAsync ()
{
var tcs = new TaskCompletionSource<int> ();
ThreadPool.QueueUserWorkItem (() => {
BuildAComputer ();
LetItRun ();
tcs.SetResult (42); // Terminate
});
return tcs.Task;
}
Task<int> GetAnswerAsync ()
{
var tcs = new TaskCompletionSource<int> ();
ThreadPool.QueueUserWorkItem (() => {
BuildAComputer ();
LetItRun ();
tcs.SetResult (42); // Terminate
});
return tcs.Task;
}
Task<int> GetAnswerAsync ()
{
var tcs = new TaskCompletionSource<int> ();
ThreadPool.QueueUserWorkItem (() => {
BuildAComputer ();
LetItRun ();
tcs.SetResult (42); // Terminate
});
return tcs.Task;
}
Task<int> GetAnswerAsync ()
{
var tcs = new TaskCompletionSource<int> ();
ThreadPool.QueueUserWorkItem (() => {
BuildAComputer ();
LetItRun ();
tcs.SetResult (42); // Terminate
});
return tcs.Task;
}
Awaiting Callbacks
Task<int> GetAnswerAsync ()
{
var tcs = new TaskCompletionSource<int> ();
GetAnswerCPS (
“input”,
(result, error) => {
if (error != null)
tcs.SetException (error);
else tcs.SetResult (result);
});
return tcs.Task;
}
Awaiting Callbacks
Task<int> GetAnswerAsync ()
{
var tcs = new TaskCompletionSource<int> ();
GetAnswerCPS (
“input”,
(result, error) => {
if (error != null)
tcs.SetException (error);
else tcs.SetResult (result);
});
return tcs.Task;
}
Awaiting Callbacks
Task<int> GetAnswerAsync ()
{
var tcs = new TaskCompletionSource<int> ();
GetAnswerCPS (
“input”,
(result, error) => {
if (error != null)
tcs.SetException (error);
else tcs.SetResult (result);
});
return tcs.Task;
}
Awaiting Callbacks
Task<int> GetAnswerAsync ()
{
var tcs = new TaskCompletionSource<int> ();
GetAnswerCPS (
“input”,
(result, error) => {
if (error != null)
tcs.SetException (error);
else tcs.SetResult (result);
});
return tcs.Task;
}
Awaiting Events
async Task EnterCode ()
{
// Must Tap buttons in the order 4, 2, 3, 3
await button4.Tapped ();
await button2.Tapped ();
await button3.Tapped ();
await button3.Tapped ();
// They get access
RevealSecretUI ();
}
Awaiting Events
static Task<EventArgs> Tapped (this UIButton btn)
{
var tcs = new TaskCompletionSource<int> ();
EventHandler handler;
handler = (s, e) => {
btn.TouchUpInside -= handler;
tcs.SetResult (e);
};
btn.TouchUpInside += handler;
return tcs.Task;
}
Awaiting Events
static Task<EventArgs> Tapped (this UIButton btn)
{
var tcs = new TaskCompletionSource<int> ();
EventHandler handler;
handler = (s, e) => {
btn.TouchUpInside -= handler;
tcs.SetResult (e);
};
btn.TouchUpInside += handler;
return tcs.Task;
}
Awaiting Events
static Task<EventArgs> Tapped (this UIButton btn)
{
var tcs = new TaskCompletionSource<int> ();
EventHandler handler;
handler = (s, e) => {
btn.TouchUpInside -= handler;
tcs.SetResult (e);
};
btn.TouchUpInside += handler;
return tcs.Task;
}
Awaiting Events
static Task<EventArgs> Tapped (this UIButton btn)
{
var tcs = new TaskCompletionSource<int> ();
EventHandler handler;
handler = (s, e) => {
btn.TouchUpInside -= handler;
tcs.SetResult (e);
};
btn.TouchUpInside += handler;
return tcs.Task;
}
Awaiting Generic Events
await button4.Tapped ();
await button4.Event (“TouchUpInside”);
Awaiting Generic Events
// Wait for the user to change the name of the
// model to “Frank”
var propEvent = await model.Event (
“PropertyChanged”,
x => x.Name == “Name” && model.Name == “Frank”);
Awaiting Generic Events
public static Task<EventArgs> Event (
this object eventSource,
string eventName,
Predicate<EventArgs> predicate = null)
{
var tcs = new TaskCompletionSource<EventArgs>();
var type = eventSource.GetType ();
var ev = type.GetEvent (eventName);
EventHandler handler;
handler = delegate (object sender, EventArgs e) {
if (predicate == null || predicate (e)) {
ev.RemoveEventHandler (eventSource, handler);
tcs.SetResult (e);
}
};
ev.AddEventHandler (eventSource, handler);
return tcs.Task;
}
Awaiting Generic Events
public static Task<EventArgs> Event (
this object eventSource,
string eventName,
Predicate<EventArgs> predicate = null)
{
var tcs = new TaskCompletionSource<EventArgs>();
var type = eventSource.GetType ();
var ev = type.GetEvent (eventName);
EventHandler handler;
handler = delegate (object sender, EventArgs e) {
if (predicate == null || predicate (e)) {
ev.RemoveEventHandler (eventSource, handler);
tcs.SetResult (e);
}
};
ev.AddEventHandler (eventSource, handler);
return tcs.Task;
}
Awaiting Generic Events
public static Task<EventArgs> Event (
this object eventSource,
string eventName,
Predicate<EventArgs> predicate = null)
{
var tcs = new TaskCompletionSource<EventArgs>();
var type = eventSource.GetType ();
var ev = type.GetEvent (eventName);
EventHandler handler;
handler = delegate (object sender, EventArgs e) {
if (predicate == null || predicate (e)) {
ev.RemoveEventHandler (eventSource, handler);
tcs.SetResult (e);
}
};
ev.AddEventHandler (eventSource, handler);
return tcs.Task;
}
Awaiting Generic Events
public static Task<EventArgs> Event (
this object eventSource,
string eventName,
Predicate<EventArgs> predicate = null)
{
var tcs = new TaskCompletionSource<EventArgs>();
var type = eventSource.GetType ();
var ev = type.GetEvent (eventName);
EventHandler handler;
handler = delegate (object sender, EventArgs e) {
if (predicate == null || predicate (e)) {
ev.RemoveEventHandler (eventSource, handler);
tcs.SetResult (e);
}
};
ev.AddEventHandler (eventSource, handler);
return tcs.Task;
}
Awaiting Generic Events
public static Task<EventArgs> Event (
this object eventSource,
string eventName,
Predicate<EventArgs> predicate = null)
{
var tcs = new TaskCompletionSource<EventArgs>();
var type = eventSource.GetType ();
var ev = type.GetEvent (eventName);
EventHandler handler;
handler = delegate (object sender, EventArgs e) {
if (predicate == null || predicate (e)) {
ev.RemoveEventHandler (eventSource, handler);
tcs.SetResult (e);
}
};
ev.AddEventHandler (eventSource, handler);
return tcs.Task;
}
button.TouchUpInside += async delegate {
var project = await ChooseProject ();
project.DoSomethingMindBlowing ();
}

AwaitingView Controllers
Task<Project> ChooseProject ()

{

var tcs = new TaskCompletionSource<Project> ();



var vc = new ChooseProjectViewController (projs);

vc.Completed += (s, e) => {

DismissViewController (true, null);

tcs.SetResult (vc.Project);

};



PresentViewController (vc, true, null);



return tcs.Task;

}

AwaitingView Controllers
Task<Project> ChooseProject ()

{

var tcs = new TaskCompletionSource<Project> ();



var vc = new ChooseProjectViewController (projs);

vc.Completed += (s, e) => {

DismissViewController (true, null);

tcs.SetResult (vc.Project);

};



PresentViewController (vc, true, null);



return tcs.Task;

}

AwaitingView Controllers
Task<Project> ChooseProject ()

{

var tcs = new TaskCompletionSource<Project> ();



var vc = new ChooseProjectViewController (projs);

vc.Completed += (s, e) => {

DismissViewController (true, null);

tcs.SetResult (vc.Project);

};



PresentViewController (vc, true, null);



return tcs.Task;

}

AwaitingView Controllers
Task<Project> ChooseProject ()

{

var tcs = new TaskCompletionSource<Project> ();



var vc = new ChooseProjectViewController (projs);

vc.Completed += (s, e) => {

DismissViewController (true, null);

tcs.SetResult (vc.Project);

};



PresentViewController (vc, true, null);



return tcs.Task;

}

AwaitingView Controllers
if (await Ask ("Archive", "Cancel") == 0) {
await Archive ();
}

Awaiting Dialogs
Task<int> Ask (params string[] answers)

{

var tcs = new TaskCompletionSource<int> ();



var sheet = new UIActionSheet ();

foreach (var a in answers)
sheet.AddButton (a);

sheet.CancelButtonIndex = answers.Length - 1;



sheet.Clicked += (s, e) => {

tcs.SetResult (e.ButtonIndex);

};



sheet.ShowFrom (View.Bounds, View, true);



return tcs.Task;

}

Awaiting Dialogs
Task<int> Ask (params string[] answers)

{

var tcs = new TaskCompletionSource<int> ();



var sheet = new UIActionSheet ();

foreach (var a in answers)
sheet.AddButton (a);

sheet.CancelButtonIndex = answers.Length - 1;



sheet.Clicked += (s, e) => {

tcs.SetResult (e.ButtonIndex);

};



sheet.ShowFrom (View.Bounds, View, true);



return tcs.Task;

}

Awaiting Dialogs
Task<int> Ask (params string[] answers)

{

var tcs = new TaskCompletionSource<int> ();



var sheet = new UIActionSheet ();

foreach (var a in answers)
sheet.AddButton (a);

sheet.CancelButtonIndex = answers.Length - 1;



sheet.Clicked += (s, e) => {

tcs.SetResult (e.ButtonIndex);

};



sheet.ShowFrom (View.Bounds, View, true);



return tcs.Task;

}

Awaiting Dialogs
Task<int> Ask (params string[] answers)

{

var tcs = new TaskCompletionSource<int> ();



var sheet = new UIActionSheet ();

foreach (var a in answers)
sheet.AddButton (a);

sheet.CancelButtonIndex = answers.Length - 1;



sheet.Clicked += (s, e) => {

tcs.SetResult (e.ButtonIndex);

};



sheet.ShowFrom (View.Bounds, View, true);



return tcs.Task;

}

Awaiting Dialogs
Task<int> Ask (params string[] answers)

{

var tcs = new TaskCompletionSource<int> ();



var sheet = new UIActionSheet ();

foreach (var a in answers)
sheet.AddButton (a);

sheet.CancelButtonIndex = answers.Length - 1;



sheet.Clicked += (s, e) => {

tcs.SetResult (e.ButtonIndex);

};



sheet.ShowFrom (View.Bounds, View, true);



return tcs.Task;

}

Awaiting Dialogs
And more...
Chaining Animations
Multi-gesture Recognition
Autonomous Services
...
Task
TaskCompletionSource
TaskScheduler
CancellationToken
TaskScheduler
Think of it as a Thread
TaskScheduler
Think of it as a Thread
it’s not though
TaskScheduler
Think of it as a Thread
it’s not though
but close enough
var tweets = await client.GetTweetsAsync ();
countLabel.Text = tweets.Count;
var tweets = await client.GetTweetsAsync ();
countLabel.Text = tweets.Count;
client.GetTweetsAsync ().ContinueWith (
t => {
if (t.IsFaulted)
throw new AggregateException (t.Exception);
countLabel.Text = tweets.Count;
},
TaskScheduler.FromCurrentSynchronizationContext ());
ConfigureAwait (false)
var tweets = await client.GetTweetsAsync ().
ConfigureAwait (false);
countLabel.Text = tweets.Count;
client.GetTweetsAsync ().ContinueWith (
t => {
if (t.IsFaulted)
throw new AggregateException (t.Exception);
countLabel.Text = tweets.Count;
});
var tweets = await client.GetTweetsAsync ().
ConfigureAwait (false);
countLabel.Text = tweets.Count;
Does not synchronize with the UI
Task
TaskCompletionSource
TaskScheduler
CancellationToken
CancellationToken
if (token.IsCancellationRequested) {
return;
}
CancellationToken
public Task LaunchTheRockets (
CancellationToken token);
CancellationTokenSource
var cts = new CancellationTokenSource ();
control.LaunchTheRockets (cts.Token);
CancellationTokenSource
var cts = new CancellationTokenSource ();
control.LaunchTheRockets (cts.Token);
// OMG, Cancel the rockets!
cts.Cancel ();
Why Async?
Why Await?
Tour of System.Threading.Tasks
with Real-world Uses of Await
Fak-world Uses of Await
Interlude
*
Map
Location
*
*
Thing
Monkey
GoldLiving Thing
Bat
*
Adventure
Player
IConsole
1
1
Adventure
Player
IConsole
1
interface IConsole
{
TextReader In { get; }
TextWriter Out { get; }
}

Adventure
public static void Main (string[] args)
{
var console = new ConsoleConsole ();
var map = Map.GenerateRandom ();
var player = new Player(
map.StartingLocation,
console);
player.Play ().Wait ();
}

Adventurepublic async Task Play ()
{
while (true) {
// Print the status
await console.Out.WriteLineAsync (Location.Description);
await console.Out.WriteLineAsync (Status);
// Read the command from the user
var command = await console.In.ReadLineAsync ();
if (!string.IsNullOrWhiteSpace (command)) {
// Execute it
var result = Execute (command);
// Print the result
await console.Out.WriteLineAsync (result);
}
}
}

Why Async?
Why Await?
Tour of System.Threading.Tasks
with Real-world Uses of Await
Fak-world Uses of Await
Recognizing
Asynchronous Events
async Task EnterCode ()
{
// Must Tap buttons in the order 4, 2, 3, 3
await button4.Tapped ();
await button2.Tapped ();
await button3.Tapped ();
await button3.Tapped ();
// They get access
RevealSecretUI ();
}
Recognizing
Asynchronous Events
Recognizing
Asynchronous Events
Recognizing
Human Time Events
Async Tutorial
async Task ShowTheUserHowToSearch ()
{
await Tutorial.EnterText
(searchField, minLength: 3);
await Tutorial.Tap
(searchButton);
await Tutorial.Congratulate
("Now you know how to search.");
}

http://praeclarum.org/post/45277337108
Recognizing
Asynchronous Events
Recognizing
Human Time Events
Web Requests are
Human Time
Turn the Web Server
Inside Out
Instead of waiting for random
requests, specify the requests
you want in the order you
want them.
public class StepByStepServer : WebServer
{
protected override async Task RunSession (WebSession session)
{
await session.Get ("/");
await session.Out.WriteLineAsync (
"<html><h1>Home</h1><a href='step1'>Next</a></html>");
await session.Get ("/hello");
await session.Out.WriteLineAsync (
"<html><h1>Hello</h1><a href='done'>Finish</a></html>");
await session.Get ("/done");
await session.Out.WriteLineAsync (
"<html><h1>Yay!</h1><a href='/'>Start Over</a></html>");
}
}




public class CounterServer : WebServer
{
protected override async Task RunSession (WebSession session)
{
for (var i = 3; i >= 1; i--) {
await session.Get ();
await session.Out.WriteAsync (i.ToString ());
}
while (true) {
await session.Get ();
await session.Out.WriteAsync ("Go MonkeySpace!");
}
}
}

Seattle? WTF?
class WebConsole : IConsole
Adventure on the Web
using the same code as
the Console
public class GameServer : WebServer
{
Map map;
public GameServer (Map map)
{
this.map = map;
}
protected override async Task RunSession (WebSession session)
{
var console = new WebConsole (session);
var player = new Player (map.StartingLocation, console);
await player.Play ();
}
}

public class GameServer : WebServer
{
Map map;
public GameServer (Map map)
{
this.map = map;
}
protected override async Task RunSession (WebSession session)
{
var console = new WebConsole (session);
var player = new Player (map.StartingLocation, console);
await player.Play ();
}
}

public class GameServer : WebServer
{
Map map;
public GameServer (Map map)
{
this.map = map;
}
protected override async Task RunSession (WebSession session)
{
var console = new WebConsole (session);
var player = new Player (map.StartingLocation, console);
await player.Play ();
}
}

Multiplayer
Thank you!
Resources
Channel 9 - http://channel9.msdn.com
Xamarin EvolveVideos - http://xamarin.com/evolve/2013
Ask Jon Skeet
Asynchronous Application Patterns in C# - MonkeySpace

Asynchronous Application Patterns in C# - MonkeySpace

  • 1.
    Asynchronous Application Patterns inC# Frank A. Krueger @praeclarum
  • 2.
    Async How C# MakesEvery Other Language Look Stupid Frank A. Krueger @praeclarum
  • 3.
    Why Async? Why Await? Tourof System.Threading.Tasks with Real-world Uses of Await Fak-world Uses of Await
  • 4.
    Why Async? Why Await? Tourof System.Threading.Tasks with Real-world Uses of Await Fak-world Uses of Await
  • 5.
    Apps are Async •Interacting with the user • Performing computations • Downloading resources from the web • Uploading actions and messages to services • Reading and writing local storage • Watching for motion changes, camera or microphone events, network connectivity, etc.
  • 6.
  • 7.
  • 8.
    You can’t doanything!
  • 9.
  • 10.
    And So Async... •.NET Events • .NET 1.0 Async (IAsyncResult)
  • 11.
    And So Async... •.NET Events • .NET 1.0 Async (IAsyncResult) • Closures! Continuation Passing Style FTW!
  • 12.
    And So Async... •.NET Events • .NET 1.0 Async (IAsyncResult) • Closures! Continuation Passing Style FTW! • IObservable<T>
  • 13.
    And So Async... •.NET Events • .NET 1.0 Async (IAsyncResult) • Closures! Continuation Passing Style FTW! • IObservable<T> ARVIS
  • 14.
    And So Async... •.NET Events • .NET 1.0 Async (IAsyncResult) • Closures! Continuation Passing Style FTW! • IObservable<T> Asynchronous Retrieval of Values from Infinite Streams
  • 15.
    And So Async... •.NET Events • .NET 1.0 Async (IAsyncResult) • Closures! Continuation Passing Style FTW! • IObservable<T> • iOS Delegates
  • 16.
    And So Async... •.NET Events • .NET 1.0 Async (IAsyncResult) • Closures! Continuation Passing Style FTW! • IObservable<T> • iOS Delegates • iOS Callbacks (Obj-C bocks)
  • 17.
    And So Async... •.NET Events • .NET 1.0 Async (IAsyncResult) • Closures! Continuation Passing Style FTW! • IObservable<T> • iOS Delegates • iOS Callbacks (Obj-C bocks) • Android Listeners, Event Interfaces
  • 18.
    Why Async? Why Await? Tourof System.Threading.Tasks with Real-world Uses of Await Fak-world Uses of Await
  • 19.
    hugMeanPeopleButton.TouchUpInside += delegate{ // Network IO var tweets = twitterClient.GetMentions (); // Computation var q = from t tweets where t.IsMean select t; // Lots of Network IO foreach (var enemy in q) { twitterClient.Reply (enemy, “Here’s a hug”); } // UI statusLabel.Text = string.Join (“, “, q.Select (x => x.Name)) + “ have been hugged”; }; ©®™ 2013 Krueger Systems, Inc. All Rights Reserved.
  • 20.
    hugMeanPeopleButton.TouchUpInside += delegate{ // Network IO var tweets = twitterClient.GetMentions (); // Computation var q = from t tweets where t.IsMean select t; // Lots of Network IO foreach (var enemy in q) { twitterClient.Reply (enemy, “Here’s a hug”); } // UI statusLabel.Text = string.Join (“, “, q.Select (x => x.Name)) + “ have been hugged”; }; Slow Slow x N Slow?
  • 21.
    hugMeanPeopleButton.TouchUpInside += delegate{ // Network IO twitterClient.GetMentions ( (tweets, tweetsError) => { if (tweetsError != null) Umm (tweetsError); // Computation var q = from t in tweets where t.IsMean select t; // This is a loop, believe it or not tweetsToHug = q.ToList (); nextTweetToHug = 0; HugTweet (); }); } List<Tweet> tweetsToHug = null; int nextTweetToHug = 0; void HugTweet () { if (tweetsToHug == null) return; // Lots of Network IO if (nextTweetToHug < tweetsToHug.Count) { var t = tweetsToHug [nextTweetToHug++]; twitterClient.Reply ( t, “Here’s a hug”, replyError => { if (replyError != null) Umm (replyError); HugTweet (); }); return; } // UI statusLabel.Text = string.Join (“, “,tweetsToHug.Select(x=>x.Name)) + “ have been hugged”; tweetsToHug = null; } Continuation Passing Style (CPS)
  • 22.
    hugMeanPeopleButton.TouchUpInside += delegate{ // Network IO twitterClient.GetMentions ( (tweets, tweetsError) => { if (tweetsError != null) Umm (tweetsError); // Computation var q = from t in tweets where t.IsMean select t; // This is a loop, believe it or not tweetsToHug = q.ToList (); nextTweetToHug = 0; HugTweet (); }); } List<Tweet> tweetsToHug = null; int nextTweetToHug = 0; void HugTweet () { if (tweetsToHug == null) return; // Lots of Network IO if (nextTweetToHug < tweetsToHug.Count) { var t = tweetsToHug [nextTweetToHug++]; twitterClient.Reply ( t, “Here’s a hug”, replyError => { if (replyError != null) Umm (replyError); HugTweet (); }); return; } // UI statusLabel.Text = string.Join (“, “,tweetsToHug.Select(x=>x.Name)) + “ have been hugged”; tweetsToHug = null; } 2 Functions?
  • 23.
    hugMeanPeopleButton.TouchUpInside += delegate{ // Network IO twitterClient.GetMentions ( (tweets, tweetsError) => { if (tweetsError != null) Umm (tweetsError); // Computation var q = from t in tweets where t.IsMean select t; // This is a loop, believe it or not tweetsToHug = q.ToList (); nextTweetToHug = 0; HugTweet (); }); } List<Tweet> tweetsToHug = null; int nextTweetToHug = 0; void HugTweet () { if (tweetsToHug == null) return; // Lots of Network IO if (nextTweetToHug < tweetsToHug.Count) { var t = tweetsToHug [nextTweetToHug++]; twitterClient.Reply ( t, “Here’s a hug”, replyError => { if (replyError != null) Umm (replyError); HugTweet (); }); return; } // UI statusLabel.Text = string.Join (“, “,tweetsToHug.Select(x=>x.Name)) + “ have been hugged”; tweetsToHug = null; } StateVariables?
  • 24.
    hugMeanPeopleButton.TouchUpInside += delegate{ // Network IO twitterClient.GetMentions ( (tweets, tweetsError) => { if (tweetsError != null) Umm (tweetsError); // Computation var q = from t in tweets where t.IsMean select t; // This is a loop, believe it or not tweetsToHug = q.ToList (); nextTweetToHug = 0; HugTweet (); }); } List<Tweet> tweetsToHug = null; int nextTweetToHug = 0; void HugTweet () { if (tweetsToHug == null) return; // Lots of Network IO if (nextTweetToHug < tweetsToHug.Count) { var t = tweetsToHug [nextTweetToHug++]; twitterClient.Reply ( t, “Here’s a hug”, replyError => { if (replyError != null) Umm (replyError); HugTweet (); }); return; } // UI statusLabel.Text = string.Join (“, “,tweetsToHug.Select(x=>x.Name)) + “ have been hugged”; tweetsToHug = null; } Subtle but Critical Assignments?
  • 25.
    hugMeanPeopleButton.TouchUpInside += delegate{ // Network IO twitterClient.GetMentions ( (tweets, tweetsError) => { if (tweetsError != null) Umm (tweetsError); // Computation var q = from t in tweets where t.IsMean select t; // This is a loop, believe it or not tweetsToHug = q.ToList (); nextTweetToHug = 0; HugTweet (); }); } List<Tweet> tweetsToHug = null; int nextTweetToHug = 0; void HugTweet () { if (tweetsToHug == null) return; // Lots of Network IO if (nextTweetToHug < tweetsToHug.Count) { var t = tweetsToHug [nextTweetToHug++]; twitterClient.Reply ( t, “Here’s a hug”, replyError => { if (replyError != null) Umm (replyError); HugTweet (); }); return; } // UI statusLabel.Text = string.Join (“, “,tweetsToHug.Select(x=>x.Name)) + “ have been hugged”; tweetsToHug = null; } Recursion?
  • 26.
    hugMeanPeopleButton.TouchUpInside += delegate{ // Network IO twitterClient.GetMentions ( (tweets, tweetsError) => { if (tweetsError != null) Umm (tweetsError); // Computation var q = from t in tweets where t.IsMean select t; // This is a loop, believe it or not tweetsToHug = q.ToList (); nextTweetToHug = 0; HugTweet (); }); } List<Tweet> tweetsToHug = null; int nextTweetToHug = 0; void HugTweet () { if (tweetsToHug == null) return; // Lots of Network IO if (nextTweetToHug < tweetsToHug.Count) { var t = tweetsToHug [nextTweetToHug++]; twitterClient.Reply ( t, “Here’s a hug”, replyError => { if (replyError != null) Umm (replyError); HugTweet (); }); return; } // UI statusLabel.Text = string.Join (“, “,tweetsToHug.Select(x=>x.Name)) + “ have been hugged”; tweetsToHug = null; } Tail End of Process in a Strange Function?
  • 27.
    hugMeanPeopleButton.TouchUpInside += delegate{ // Network IO twitterClient.GetMentions ( (tweets, tweetsError) => { if (tweetsError != null) Umm (tweetsError); // Computation var q = from t in tweets where t.IsMean select t; // This is a loop, believe it or not tweetsToHug = q.ToList (); nextTweetToHug = 0; HugTweet (); }); } List<Tweet> tweetsToHug = null; int nextTweetToHug = 0; void HugTweet () { if (tweetsToHug == null) return; // Lots of Network IO if (nextTweetToHug < tweetsToHug.Count) { var t = tweetsToHug [nextTweetToHug++]; twitterClient.Reply ( t, “Here’s a hug”, replyError => { if (replyError != null) Umm (replyError); HugTweet (); }); return; } // UI statusLabel.Text = string.Join (“, “,tweetsToHug.Select(x=>x.Name)) + “ have been hugged”; tweetsToHug = null; } Umm...
  • 28.
    Our Languages Were NotDesigned for Asynchronous Procedures
  • 29.
  • 30.
  • 31.
  • 32.
    hugMeanPeopleButton.TouchUpInside += delegate{ // Network IO var tweets = twitterClient.GetMentions (); // Computation var q = from t tweets where t.IsMean select t; // Lots of Network IO foreach (var enemy in q) { twitterClient.Reply (t, “Here’s a hug”); } // UI statusLabel.Text = string.Join (“, “, q.Select (x => x.Name)) + “ have been hugged”; };
  • 33.
    hugMeanPeopleButton.TouchUpInside += asyncdelegate { // Network IO var tweets = await twitterClient.GetMentionsAsync (); // Computation var q = from t tweets where t.IsMean select t; // Lots of Network IO foreach (var enemy in q) { await twitterClient.ReplyAsync (t, “Here’s a hug”); } // UI statusLabel.Text = string.Join (“, “, q.Select (x => x.Name)) + “ have been hugged”; };
  • 34.
    hugMeanPeopleButton.TouchUpInside += asyncdelegate { // Network IO var tweets = await twitterClient.GetMentionsAsync (); // Computation var q = from t tweets where t.IsMean select t; // Lots of Network IO foreach (var enemy in q) { await twitterClient.ReplyAsync (t, “Here’s a hug”); } // UI statusLabel.Text = string.Join (“, “, q.Select (x => x.Name)) + “ have been hugged”; };
  • 35.
    hugMeanPeopleButton.TouchUpInside += asyncdelegate { // Network IO var tweets = await twitterClient.GetMentionsAsync (); // Computation, in the Background, dood var q = await Task.Run (() => from t tweets where t.IsMean select t); // Lots of Network IO foreach (var enemy in q) { await twitterClient.ReplyAsync (t, “Here’s a hug”); } // UI statusLabel.Text = string.Join (“, “, q.Select (x => x.Name)) + “ have been hugged”; };
  • 36.
  • 37.
    Why Async? Why Await? Tourof System.Threading.Tasks with Real-world Uses of Await Fak-world Uses of Await
  • 38.
  • 39.
    System.Threading.Task Parallel Process await task;// Async task.Wait (); // Synchronous
  • 40.
    System.Threading.Task Fallible Parallel Process try{ await task; // Async task.Wait (); // Synchronous } catch (Exception) { }
  • 41.
    System.Threading.Task<T> Parallel Process That ReturnsaValue var value = await task; // Async task.Wait (); // Synchronous var value = task.Result;
  • 42.
    Where do Taskscome from?
  • 43.
    var tweets =await client.GetTweetsAsync (); await client.PostAsync (“Hello, World!”); Call an Async Method
  • 44.
    var tweets =await client.GetTweetsAsync (); await client.PostAsync (“Hello, World!”); Call an Async Method Network? CPU? UI?
  • 45.
    var value =await Task.Run (() => { return 42; }); await Task.Run (() => { LaunchMissiles (); }); Start a Background Task Using a Function
  • 46.
  • 47.
    mesh = awaitTask.Run (() => sphere.Tesselate (opts));
  • 48.
    async Task<int> GetPopularityAsync() { var followers = await twitter.GetFollowersAsync (); return followers.Sum ( x => x.FollowerCount); } Methods That Use await Return Tasks
  • 49.
    var value =await Task.Factory.FromAsync ( foo.BeginDoingSomething, foo.EndDoingSomething, null); From .NET 1.0 Async
  • 50.
    await Task.Factory.ContinueWhenAll ( downloadTasks, ts=> {}); Combinators await Task.Factory.ContinueWhenAny ( actionStartGestures, t => {});
  • 51.
    await Task.Factory.ContinueWhenAll ( downloadTasks, ts=> {}); Combinators await Task.Factory.ContinueWhenAny ( actionStartGestures, t => {}); Take Multiple Tasks and turn them into One Task
  • 52.
    async Task UpdatePodcasts() { ShowUpdatingUI (); foreach (var feed in feeds) { await feed.Update (); } ShowUpdatingCompletedUI (); } Concurrency?
  • 53.
    async Task UpdatePodcasts() { ShowUpdatingUI (); foreach (var feed in feeds) { await feed.Update (); } ShowUpdatingCompletedUI (); } Concurrency? Sequential
  • 54.
    async Task UpdatePodcasts() { ShowUpdatingUI (); Task.Factory.ContinueWhenAll ( feeds.Select (x => x.Update ()), null); ShowUpdatingCompletedUI (); } Concurrency! Parallel
  • 55.
  • 56.
  • 57.
    TaskCompletionSource<T> Great way tointeract with ancient, old, fuddy duddy, outdated async models
  • 58.
  • 59.
    Task<int> GetAnswerAsync () { vartcs = new TaskCompletionSource<int> (); ThreadPool.QueueUserWorkItem (() => { BuildAComputer (); LetItRun (); tcs.SetResult (42); // Terminate }); return tcs.Task; }
  • 60.
    Task<int> GetAnswerAsync () { vartcs = new TaskCompletionSource<int> (); ThreadPool.QueueUserWorkItem (() => { BuildAComputer (); LetItRun (); tcs.SetResult (42); // Terminate }); return tcs.Task; }
  • 61.
    Task<int> GetAnswerAsync () { vartcs = new TaskCompletionSource<int> (); ThreadPool.QueueUserWorkItem (() => { BuildAComputer (); LetItRun (); tcs.SetResult (42); // Terminate }); return tcs.Task; }
  • 62.
    Task<int> GetAnswerAsync () { vartcs = new TaskCompletionSource<int> (); ThreadPool.QueueUserWorkItem (() => { BuildAComputer (); LetItRun (); tcs.SetResult (42); // Terminate }); return tcs.Task; }
  • 63.
    Task<int> GetAnswerAsync () { vartcs = new TaskCompletionSource<int> (); ThreadPool.QueueUserWorkItem (() => { BuildAComputer (); LetItRun (); tcs.SetResult (42); // Terminate }); return tcs.Task; }
  • 64.
    Awaiting Callbacks Task<int> GetAnswerAsync() { var tcs = new TaskCompletionSource<int> (); GetAnswerCPS ( “input”, (result, error) => { if (error != null) tcs.SetException (error); else tcs.SetResult (result); }); return tcs.Task; }
  • 65.
    Awaiting Callbacks Task<int> GetAnswerAsync() { var tcs = new TaskCompletionSource<int> (); GetAnswerCPS ( “input”, (result, error) => { if (error != null) tcs.SetException (error); else tcs.SetResult (result); }); return tcs.Task; }
  • 66.
    Awaiting Callbacks Task<int> GetAnswerAsync() { var tcs = new TaskCompletionSource<int> (); GetAnswerCPS ( “input”, (result, error) => { if (error != null) tcs.SetException (error); else tcs.SetResult (result); }); return tcs.Task; }
  • 67.
    Awaiting Callbacks Task<int> GetAnswerAsync() { var tcs = new TaskCompletionSource<int> (); GetAnswerCPS ( “input”, (result, error) => { if (error != null) tcs.SetException (error); else tcs.SetResult (result); }); return tcs.Task; }
  • 68.
    Awaiting Events async TaskEnterCode () { // Must Tap buttons in the order 4, 2, 3, 3 await button4.Tapped (); await button2.Tapped (); await button3.Tapped (); await button3.Tapped (); // They get access RevealSecretUI (); }
  • 69.
    Awaiting Events static Task<EventArgs>Tapped (this UIButton btn) { var tcs = new TaskCompletionSource<int> (); EventHandler handler; handler = (s, e) => { btn.TouchUpInside -= handler; tcs.SetResult (e); }; btn.TouchUpInside += handler; return tcs.Task; }
  • 70.
    Awaiting Events static Task<EventArgs>Tapped (this UIButton btn) { var tcs = new TaskCompletionSource<int> (); EventHandler handler; handler = (s, e) => { btn.TouchUpInside -= handler; tcs.SetResult (e); }; btn.TouchUpInside += handler; return tcs.Task; }
  • 71.
    Awaiting Events static Task<EventArgs>Tapped (this UIButton btn) { var tcs = new TaskCompletionSource<int> (); EventHandler handler; handler = (s, e) => { btn.TouchUpInside -= handler; tcs.SetResult (e); }; btn.TouchUpInside += handler; return tcs.Task; }
  • 72.
    Awaiting Events static Task<EventArgs>Tapped (this UIButton btn) { var tcs = new TaskCompletionSource<int> (); EventHandler handler; handler = (s, e) => { btn.TouchUpInside -= handler; tcs.SetResult (e); }; btn.TouchUpInside += handler; return tcs.Task; }
  • 73.
    Awaiting Generic Events awaitbutton4.Tapped (); await button4.Event (“TouchUpInside”);
  • 74.
    Awaiting Generic Events //Wait for the user to change the name of the // model to “Frank” var propEvent = await model.Event ( “PropertyChanged”, x => x.Name == “Name” && model.Name == “Frank”);
  • 75.
    Awaiting Generic Events publicstatic Task<EventArgs> Event ( this object eventSource, string eventName, Predicate<EventArgs> predicate = null) { var tcs = new TaskCompletionSource<EventArgs>(); var type = eventSource.GetType (); var ev = type.GetEvent (eventName); EventHandler handler; handler = delegate (object sender, EventArgs e) { if (predicate == null || predicate (e)) { ev.RemoveEventHandler (eventSource, handler); tcs.SetResult (e); } }; ev.AddEventHandler (eventSource, handler); return tcs.Task; }
  • 76.
    Awaiting Generic Events publicstatic Task<EventArgs> Event ( this object eventSource, string eventName, Predicate<EventArgs> predicate = null) { var tcs = new TaskCompletionSource<EventArgs>(); var type = eventSource.GetType (); var ev = type.GetEvent (eventName); EventHandler handler; handler = delegate (object sender, EventArgs e) { if (predicate == null || predicate (e)) { ev.RemoveEventHandler (eventSource, handler); tcs.SetResult (e); } }; ev.AddEventHandler (eventSource, handler); return tcs.Task; }
  • 77.
    Awaiting Generic Events publicstatic Task<EventArgs> Event ( this object eventSource, string eventName, Predicate<EventArgs> predicate = null) { var tcs = new TaskCompletionSource<EventArgs>(); var type = eventSource.GetType (); var ev = type.GetEvent (eventName); EventHandler handler; handler = delegate (object sender, EventArgs e) { if (predicate == null || predicate (e)) { ev.RemoveEventHandler (eventSource, handler); tcs.SetResult (e); } }; ev.AddEventHandler (eventSource, handler); return tcs.Task; }
  • 78.
    Awaiting Generic Events publicstatic Task<EventArgs> Event ( this object eventSource, string eventName, Predicate<EventArgs> predicate = null) { var tcs = new TaskCompletionSource<EventArgs>(); var type = eventSource.GetType (); var ev = type.GetEvent (eventName); EventHandler handler; handler = delegate (object sender, EventArgs e) { if (predicate == null || predicate (e)) { ev.RemoveEventHandler (eventSource, handler); tcs.SetResult (e); } }; ev.AddEventHandler (eventSource, handler); return tcs.Task; }
  • 79.
    Awaiting Generic Events publicstatic Task<EventArgs> Event ( this object eventSource, string eventName, Predicate<EventArgs> predicate = null) { var tcs = new TaskCompletionSource<EventArgs>(); var type = eventSource.GetType (); var ev = type.GetEvent (eventName); EventHandler handler; handler = delegate (object sender, EventArgs e) { if (predicate == null || predicate (e)) { ev.RemoveEventHandler (eventSource, handler); tcs.SetResult (e); } }; ev.AddEventHandler (eventSource, handler); return tcs.Task; }
  • 80.
    button.TouchUpInside += asyncdelegate { var project = await ChooseProject (); project.DoSomethingMindBlowing (); }
 AwaitingView Controllers
  • 81.
    Task<Project> ChooseProject ()
 {
 vartcs = new TaskCompletionSource<Project> ();
 
 var vc = new ChooseProjectViewController (projs);
 vc.Completed += (s, e) => {
 DismissViewController (true, null);
 tcs.SetResult (vc.Project);
 };
 
 PresentViewController (vc, true, null);
 
 return tcs.Task;
 }
 AwaitingView Controllers
  • 82.
    Task<Project> ChooseProject ()
 {
 vartcs = new TaskCompletionSource<Project> ();
 
 var vc = new ChooseProjectViewController (projs);
 vc.Completed += (s, e) => {
 DismissViewController (true, null);
 tcs.SetResult (vc.Project);
 };
 
 PresentViewController (vc, true, null);
 
 return tcs.Task;
 }
 AwaitingView Controllers
  • 83.
    Task<Project> ChooseProject ()
 {
 vartcs = new TaskCompletionSource<Project> ();
 
 var vc = new ChooseProjectViewController (projs);
 vc.Completed += (s, e) => {
 DismissViewController (true, null);
 tcs.SetResult (vc.Project);
 };
 
 PresentViewController (vc, true, null);
 
 return tcs.Task;
 }
 AwaitingView Controllers
  • 84.
    Task<Project> ChooseProject ()
 {
 vartcs = new TaskCompletionSource<Project> ();
 
 var vc = new ChooseProjectViewController (projs);
 vc.Completed += (s, e) => {
 DismissViewController (true, null);
 tcs.SetResult (vc.Project);
 };
 
 PresentViewController (vc, true, null);
 
 return tcs.Task;
 }
 AwaitingView Controllers
  • 85.
    if (await Ask("Archive", "Cancel") == 0) { await Archive (); }
 Awaiting Dialogs
  • 86.
    Task<int> Ask (paramsstring[] answers)
 {
 var tcs = new TaskCompletionSource<int> ();
 
 var sheet = new UIActionSheet ();
 foreach (var a in answers) sheet.AddButton (a);
 sheet.CancelButtonIndex = answers.Length - 1;
 
 sheet.Clicked += (s, e) => {
 tcs.SetResult (e.ButtonIndex);
 };
 
 sheet.ShowFrom (View.Bounds, View, true);
 
 return tcs.Task;
 }
 Awaiting Dialogs
  • 87.
    Task<int> Ask (paramsstring[] answers)
 {
 var tcs = new TaskCompletionSource<int> ();
 
 var sheet = new UIActionSheet ();
 foreach (var a in answers) sheet.AddButton (a);
 sheet.CancelButtonIndex = answers.Length - 1;
 
 sheet.Clicked += (s, e) => {
 tcs.SetResult (e.ButtonIndex);
 };
 
 sheet.ShowFrom (View.Bounds, View, true);
 
 return tcs.Task;
 }
 Awaiting Dialogs
  • 88.
    Task<int> Ask (paramsstring[] answers)
 {
 var tcs = new TaskCompletionSource<int> ();
 
 var sheet = new UIActionSheet ();
 foreach (var a in answers) sheet.AddButton (a);
 sheet.CancelButtonIndex = answers.Length - 1;
 
 sheet.Clicked += (s, e) => {
 tcs.SetResult (e.ButtonIndex);
 };
 
 sheet.ShowFrom (View.Bounds, View, true);
 
 return tcs.Task;
 }
 Awaiting Dialogs
  • 89.
    Task<int> Ask (paramsstring[] answers)
 {
 var tcs = new TaskCompletionSource<int> ();
 
 var sheet = new UIActionSheet ();
 foreach (var a in answers) sheet.AddButton (a);
 sheet.CancelButtonIndex = answers.Length - 1;
 
 sheet.Clicked += (s, e) => {
 tcs.SetResult (e.ButtonIndex);
 };
 
 sheet.ShowFrom (View.Bounds, View, true);
 
 return tcs.Task;
 }
 Awaiting Dialogs
  • 90.
    Task<int> Ask (paramsstring[] answers)
 {
 var tcs = new TaskCompletionSource<int> ();
 
 var sheet = new UIActionSheet ();
 foreach (var a in answers) sheet.AddButton (a);
 sheet.CancelButtonIndex = answers.Length - 1;
 
 sheet.Clicked += (s, e) => {
 tcs.SetResult (e.ButtonIndex);
 };
 
 sheet.ShowFrom (View.Bounds, View, true);
 
 return tcs.Task;
 }
 Awaiting Dialogs
  • 91.
    And more... Chaining Animations Multi-gestureRecognition Autonomous Services ...
  • 92.
  • 93.
  • 94.
    TaskScheduler Think of itas a Thread it’s not though
  • 95.
    TaskScheduler Think of itas a Thread it’s not though but close enough
  • 96.
    var tweets =await client.GetTweetsAsync (); countLabel.Text = tweets.Count;
  • 97.
    var tweets =await client.GetTweetsAsync (); countLabel.Text = tweets.Count; client.GetTweetsAsync ().ContinueWith ( t => { if (t.IsFaulted) throw new AggregateException (t.Exception); countLabel.Text = tweets.Count; }, TaskScheduler.FromCurrentSynchronizationContext ());
  • 98.
  • 99.
    var tweets =await client.GetTweetsAsync (). ConfigureAwait (false); countLabel.Text = tweets.Count;
  • 100.
    client.GetTweetsAsync ().ContinueWith ( t=> { if (t.IsFaulted) throw new AggregateException (t.Exception); countLabel.Text = tweets.Count; }); var tweets = await client.GetTweetsAsync (). ConfigureAwait (false); countLabel.Text = tweets.Count; Does not synchronize with the UI
  • 101.
  • 102.
  • 103.
  • 104.
    CancellationTokenSource var cts =new CancellationTokenSource (); control.LaunchTheRockets (cts.Token);
  • 105.
    CancellationTokenSource var cts =new CancellationTokenSource (); control.LaunchTheRockets (cts.Token); // OMG, Cancel the rockets! cts.Cancel ();
  • 106.
    Why Async? Why Await? Tourof System.Threading.Tasks with Real-world Uses of Await Fak-world Uses of Await Interlude
  • 107.
  • 108.
  • 109.
    Adventure public static voidMain (string[] args) { var console = new ConsoleConsole (); var map = Map.GenerateRandom (); var player = new Player( map.StartingLocation, console); player.Play ().Wait (); }

  • 110.
    Adventurepublic async TaskPlay () { while (true) { // Print the status await console.Out.WriteLineAsync (Location.Description); await console.Out.WriteLineAsync (Status); // Read the command from the user var command = await console.In.ReadLineAsync (); if (!string.IsNullOrWhiteSpace (command)) { // Execute it var result = Execute (command); // Print the result await console.Out.WriteLineAsync (result); } } }

  • 111.
    Why Async? Why Await? Tourof System.Threading.Tasks with Real-world Uses of Await Fak-world Uses of Await
  • 112.
  • 113.
    async Task EnterCode() { // Must Tap buttons in the order 4, 2, 3, 3 await button4.Tapped (); await button2.Tapped (); await button3.Tapped (); await button3.Tapped (); // They get access RevealSecretUI (); } Recognizing Asynchronous Events
  • 114.
  • 115.
    Async Tutorial async TaskShowTheUserHowToSearch () { await Tutorial.EnterText (searchField, minLength: 3); await Tutorial.Tap (searchButton); await Tutorial.Congratulate ("Now you know how to search."); }
 http://praeclarum.org/post/45277337108
  • 116.
  • 117.
  • 118.
    Turn the WebServer Inside Out Instead of waiting for random requests, specify the requests you want in the order you want them.
  • 119.
    public class StepByStepServer: WebServer { protected override async Task RunSession (WebSession session) { await session.Get ("/"); await session.Out.WriteLineAsync ( "<html><h1>Home</h1><a href='step1'>Next</a></html>"); await session.Get ("/hello"); await session.Out.WriteLineAsync ( "<html><h1>Hello</h1><a href='done'>Finish</a></html>"); await session.Get ("/done"); await session.Out.WriteLineAsync ( "<html><h1>Yay!</h1><a href='/'>Start Over</a></html>"); } } 
 

  • 120.
    public class CounterServer: WebServer { protected override async Task RunSession (WebSession session) { for (var i = 3; i >= 1; i--) { await session.Get (); await session.Out.WriteAsync (i.ToString ()); } while (true) { await session.Get (); await session.Out.WriteAsync ("Go MonkeySpace!"); } } }

  • 121.
  • 122.
  • 123.
    Adventure on theWeb using the same code as the Console
  • 124.
    public class GameServer: WebServer { Map map; public GameServer (Map map) { this.map = map; } protected override async Task RunSession (WebSession session) { var console = new WebConsole (session); var player = new Player (map.StartingLocation, console); await player.Play (); } }

  • 125.
    public class GameServer: WebServer { Map map; public GameServer (Map map) { this.map = map; } protected override async Task RunSession (WebSession session) { var console = new WebConsole (session); var player = new Player (map.StartingLocation, console); await player.Play (); } }

  • 126.
    public class GameServer: WebServer { Map map; public GameServer (Map map) { this.map = map; } protected override async Task RunSession (WebSession session) { var console = new WebConsole (session); var player = new Player (map.StartingLocation, console); await player.Play (); } }

  • 127.
  • 128.
  • 129.
    Resources Channel 9 -http://channel9.msdn.com Xamarin EvolveVideos - http://xamarin.com/evolve/2013 Ask Jon Skeet