20. Event-based Async Model
class XxxCompletedEventArgs : EventArgs {
Exception Error { get; }
TResult Result { get; }
}
class Worker {
event EventHandler<XxxCompletedArgs> XxxCompleted;
void XxxAsync(arg1, arg2, ...);
}
21.
22. Code Locality is Broken
• Used to expressing algorithms linearly
• Async requires logical division of algorithms
• No if / using / while / for ...
• Very difficult to
• Combine multiple asynchronous operations
• Deal with exceptions and cancellation
29. Async with “yield”
IEnumerator<int> Transfer(AsyncEnumerator ae, string url, Stream streamOut) {
var request = WebRequest.Create(url);
request.BeginGetResponse(ae.End(), null);
yield return 1;
using (var response = request.EndGetResponse(ae.DequeueAsyncResult())) {
var streamIn = response.GetResponseStream();
byte[] buffer = new byte[1024];
while (true) {
streamIn.BeginRead(buffer, 0, buffer.Length, ae.End(), null);
yield return 1;
int lengthRead = streamIn.EndRead(ae.DequeueAsyncResult());
if (lengthRead <= 0) break;
streamOut.BeginWrite(buffer, 0, lengthRead, ae.End(), null);
yield return 1;
streamOut.EndWrite(ae.DequeueAsyncResult());
}
}
}
30. Async with “yield”
IEnumerator<int> Transfer(AsyncEnumerator ae, string url, Stream streamOut) {
var request = WebRequest.Create(url);
request.BeginGetResponse(ae.End(), null);
yield return 1;
using (var response = request.EndGetResponse(ae.DequeueAsyncResult())) {
var streamIn = response.GetResponseStream();
byte[] buffer = new byte[1024];
while (true) {
streamIn.BeginRead(buffer, 0, buffer.Length, ae.End(), null);
“yield” after yield return 1;
firing async
operations
int lengthRead = streamIn.EndRead(ae.DequeueAsyncResult());
if (lengthRead <= 0) break;
streamOut.BeginWrite(buffer, 0, lengthRead, ae.End(), null);
yield return 1;
streamOut.EndWrite(ae.DequeueAsyncResult());
}
}
}
31. Async with “yield”
IEnumerator<int> Transfer(AsyncEnumerator ae, string url, Stream streamOut) {
var request = WebRequest.Create(url);
request.BeginGetResponse(ae.End(), null);
yield return 1;
using (var response = request.EndGetResponse(ae.DequeueAsyncResult())) {
var streamIn = response.GetResponseStream();
byte[] buffer = new byte[1024];
while (true) {
streamIn.BeginRead(buffer, 0, buffer.Length, ae.End(), null);
“yield” after yield return 1;
firing async
operations
int lengthRead = streamIn.EndRead(ae.DequeueAsyncResult());
if (lengthRead <= 0) break;
streamOut.BeginWrite(buffer, 0, lengthRead, ae.End(), null);
yield return 1;
streamOut.EndWrite(ae.DequeueAsyncResult()); continue when
} async operations
} completed
}
32. “yield return” for Async
• Coming with new programming patterns
• Keep code locality
• Good parts: support if / using / while / for ...
• But not perfect: cannot use try...catch
• The primitives for Fibers - lightweight
computation units
34. F#
• Language by Don Syme, MS Research
• Strongly statically typed language
• Functional language with OO ability
• For industry and education
• Open source (Apache 2.0)
• Cross-platform supported by Microsoft
36. What’s async { ... }
... the principle we go by is, don't expect to see
a particular concurrency model put into C#
because there're many different concurrency
model ... it's more about finding things are
common to to all kinds of concurrency ...
- Anders Hejlsberg
39. Async Workflow
React!
async {
let! res = <async work>
...
}
an HTTP Response
an UI Event
a Timer Callback
a Query Response
a Web Servcie Response
a Disk I/O Completion
an Agent Message
40. Async in F#
let transferAsync (url: string) (streamOut: Stream) = async {
let request = WebRequest.Create(url)
use! response = request.AsyncGetResponse()
let streamIn = response.GetResponseStream()
let rec transferDataAsync (buffer: byte[]) = async {
let! lengthRead = streamIn.AsyncRead(buffer, 0, buffer.Length)
if lengthRead > 0 then
do! streamOut.AsyncWrite(buffer, 0, lengthRead)
do! transferDataAsync buffer
}
do! transferDataAsync (Array.zeroCreate 1024)
}
41. Async in F#
let transferAsync (url: string) (streamOut: Stream) = async {
let request = WebRequest.Create(url)
use! response = request.AsyncGetResponse()
AsyncGetResponse
let streamIn = response.GetResponseStream()
let rec transferDataAsync (buffer: byte[]) = async {
let! lengthRead = streamIn.AsyncRead(buffer, 0, buffer.Length)
AsyncRead
if lengthRead > 0 then
do! streamOut.AsyncWrite(buffer, 0, lengthRead)
AsyncWrite
do! transferDataAsync buffer
}
simply generate from
do! transferDataAsync (Array.zeroCreate 1024) Begin/End methods
}
44. F# Async Workflow
• Library, not a language feature
• Based on Computation Expressions in F#
• Support all kinds of language constructions
• Error handling: try...catch
• Loop: while / for (like “foreach” in C#)
• Others: if / use (like “using” in C#), etc.
• Easy to
• Combine multiple asynchronous operations
• Deal with exceptions and cancellation
45. F# Resources
http://fsharp.net
Programming F# Expert F# 2.0 Real World FP
53. IEnumerable & IEnumerator are prototypical
interfaces for interactive collections and
interactive programs.
IObservable & IObserver are prototypical
interfaces for observable collections and
reactive, asynchronous & event-based programs.
54. LINQ to Observable
If you are writing
LINQ or declarative code
in an interactive program...
55. LINQ to Observable
If you are writing
LINQ or declarative code
in an interactive program...
You already know how to use it!
56. Again
... the principle we go by is, don't expect to see
a particular concurrency model put into C#
because there're many different concurrency
model ... it's more about finding things are
common to to all kinds of concurrency ...
- Anders Hejlsberg
57. Again
... the principle we go by is, don't expect to see
a particular concurrency model put into C#
because there're many different concurrency
model ... it's more about finding things are
common to to all kinds of concurrency ...
- Anders Hejlsberg
58. Move by WASD
// filter the KeyPress events when playing
var keyPress = GetKeyPress().Where(_ => isPlaying);
// filter the events to move left
var moveLeft = from ev in keyPress
where ev.EventArgs.KeyChar == 'a'
where ball.Left > 0
select ev;
moveLeft.Subscribe(_ => ball.Left -= 5);
// filter the events to move top
var moveTop = from ev in keyPress
where ev.EventArgs.KeyChar == 'w'
where ball.Top > 0
select ev;
moveTop.Subscribe(_ => ball.Top -= 5);
59. Mouse Drag and Draw
var mouseMove = GetMouseMove();
var mouseDiff = mouseMove.Zip(mouseMove.Skip(1),
(prev, curr) => new
{
PrevPos = new Point(
prev.EventArgs.X, prev.EventArgs.Y),
CurrPos = new Point(
curr.EventArgs.X, curr.EventArgs.Y)
});
var mouseDrag = from _ in GetMouseDown()
from diff in mouseDiff.TakeUntil(GetMouseUp())
select diff;
mouseDrag.Subscribe(d => DrawLine(d.PrevPos, d.CurrPos));
60. Everything in Web is
Asynchronous
• User actions
• AJAX requests
• Animations
• ...
61. Rx in JavaScript
• A full featured port for JavaScript
• Easy-to-use conversions from existing DOM,
XmlHttpRequest, etc
• In a download size of less than 7kb (gzipped)
• Bindings for various libraries / frameworks
• jQuery
• MooTools
• Dojo
• ...
62. Drag and Draw in JS
var target = $("#paintPad");
var mouseMove = target.toObservable("mousemove");
var mouseDiff = mouseMove.Zip(mouseMove.Skip(1),
function(prev, curr) {
return {
prevPos: { x: prev.clientX, y: prev.clientY },
currPos: { x: curr.clientX, y: curr.clientY }
};
});
var mouseDown = target.toObservable("mousedown");
var mouseUp = target.toObservable("mouseup");
var mouseDrag = mouseDown.SelectMany(function() {
mouseDiff.TakeUntil(mouseUp);
});
mouseDrag.Subscribe(
function(d) { drawLine(d.prevPos, d.currPos); });
64. Time Flies like an Arrow
var container = $("#container");
var mouseMove = container.toObservable("mousemove");
for (var i = 0; i < text.length; i++) {
(function(i) {
var ele = $("<span/>").text(text.charAt(i));
ele.css({position: "absolute"}).appendTo(container);
mouseMove.Delay(i * 100).Subscribe(function (ev) {
ele.css({
left: ev.clientX + i * 20 + 15 + "px",
top: ev.clientY + "px"
});
});
})(i);
65. Benefits of Rx
• Easy to composite and coordinate async
operations
• Express the algorithm in functional ways
• Helper method: For / While / If / Try / Switch...
• Easy to be unit tested
• ...
66. Rx & Language Features
• Features in C# that Rx uses
• Extension method
• Lambda expression & closure
• Type inference
• LINQ query expression
• Rx has been implemented in ...
• C# & VB
• JavaScript
• F#
67. Portability
• Rx can be easily ported to various languages
• Scala
• Ruby
• Python
• modern languages with basic functional features
• Almost impossible to implement Rx in Java
• Cannot extend a type without breaking code
• Missing enough functional features
68. Rx Resources
• Matthew Podwysocki
• http://codebetter.com/blogs/matthew.podwysocki/
• Reactive Framework on MSDN DevLabs
• http://msdn.microsoft.com/en-us/devlabs/
ee794896.aspx
• Tomáš Petříček
• http://tomasp.net/
69. Comparison
• F# Async Workflow
• Elegant, simple, easy to use
• Can only be used at server-side (WebSharper come to
rescure?)
• Reactive Framework
• Can be used at both server-side and client-side.
• New async model brings learning cost.
75. Source
async Task<XElement> GetRssAsync(string url) {
var client = new WebClient();
var task = client.DownloadStringTaskAsync(url);
var text = await task;
var xml = XElement.Parse(text);
return xml;
}
76. Compiled
Task<XElement> GetRssAsync(string url) {
var $builder = AsyncMethodBuilder<XElement>.Create();
var $state = 0;
TaskAwaiter<string> $a1;
Action $resume = delegate {
try {
if ($state == 1) goto L1;
var client = new WebClient();
var task = client.DownloadStringTaskAsync(url);
$state = 1;
$a1 = task.GetAwaiter();
if ($a1.BeginAwait($resume)) return;
L1: var text = $a1.EndAwait();
var xml = XElement.Parse(text);
$builder.SetResult(xml);
}
catch (Exception $ex) { $builder.SetException($ex); }
};
$resume();
return $builder.Task;
}