Rethinking Asynchronous Programming Note

01 - Course Introduction

Async Patterns

  • Parallel vs Async
  • Callbacks
  • Thunks
  • Promises
  • Generators/Coroutines
  • Event Reactive (observables)
  • CSP (channel-oriented concurrency)

02 - Single Threaded JavaScript

Parallel vs Async

Threads
Single Thread

  • Everything in JavaScript is happening on a single thread: your JS, your styles, your garbage collection, etc. This is why you get jitters in the UI. When your UI is trying to repaint at 60fps and something else requires computation, you get a momentary jitter.
  • Things happen concurrently in JavaScript to avoid unnecessary stalls in computer/user interaction. It’d be weird if you made a synchronous Ajax call which made the page freeze up until the request was completely done. Instead, Ajax calls happen concurrently with other tasks (like your UI repainting), even though at any given instant only one of those tasks can be executing on the tread (i.e. event loop).
  • 『Asynchronous programming』 in JavaScript is better patterns for concurrency management, i.e. patterns on top of the existing pattern we have for concurrency (callbacks) like generators and promises.

03 - Concurrency

04 - Callback Hell

callbacks

1
2
3
setTimeout(function(){
console.log('callback')
}, 1000)

callbacks == continuations

Callbacks are also called “continuations” because they are code continuations in the sense that you are executing code now and then later. That “later” could be any time, for example when a network call is finished. But the idea is that you want some code to run now and you want some code to run later. And when that 『later』 code runs, you essentially want to pick up where you left off from before. This is why callbacks are often called “continuations”.

callback hell

1
2
3
4
5
6
7
8
9
setTimeout(function(){
console.log('one')
setTimeout(function(){
console.log('two')
setTimeout(function(){
console.log('three')
}, 1000)
}, 1000)
}, 1000)

Is callback hell indentation and nesting?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function one(cb) {
console.log('one')
setTimeout(cb, 1000)
}

function two(cb) {
console.log('two')
setTimeout(cb, 1000)
}

function three(cb) {
console.log('three')
setTimeout(cb, 1000)
}

one(function() {
two(three)
})

Callback hell has nothing to do with the indentation of your code, it’s deeper than syntax.

Callback hell has to do with an inversion of control, which means there’s a part of my code I’m responsible for executing (the “do it now” stuff) and there’s part of my code that I’m not in charge of executing (the “do it later” stuff, i.e. got a response from server? Now do something). So in other words, when I give somebody a callback I’m saying “here you go, now you are in control of when this thing executes”. You are trusting that the callback will be called:

  • Not too early
  • Not too late
  • Not too many times
  • Not too few times
  • No lost context
  • No swallowed errors
  • …and more!

In other words, every callback you’ve ever written has an existing bug of trust (even if you haven’t been bitten by it yet)

05 - Exercise 1

Instructions

  1. This exercise calls for you to write some async flow-control code. To start off with, you’ll use callbacks only.

  2. Expected behavior:

    • Request all 3 files at the same time (in “parallel”).
    • Render them ASAP (don’t just blindly wait for all to finish loading)
    • BUT, render them in proper (obvious) order: “file1”, “file2”, “file3”.
    • After all 3 are done, output “Complete!”.

code setup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function fakeAjax(url,cb) {
var fake_responses = {
"file1": "The first text",
"file2": "The middle text",
"file3": "The last text"
};
var randomDelay = (Math.round(Math.random() * 1E4) % 8000) + 1000;

console.log("Requesting: " + url);

setTimeout(function(){
cb(fake_responses[url]);
},randomDelay);
}

function output(text) {
console.log(text);
}

// **************************************
// The old-n-busted callback way

function getFile(file) {
fakeAjax(file,function(text){
// what do we do here?
});
}

// request all files at once in "parallel"
getFile("file1");
getFile("file2");
getFile("file3");

06 - Exercise 1 Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
function fakeAjax(url, cb) {
var fake_responses = {
"file1": "The first text",
"file2": "The middle text",
"file3": "The last text"
};
var randomDelay = (Math.round(Math.random() * 1E4) % 8000) + 1000;

console.log("Requesting: " + url);

setTimeout(function () {
cb(fake_responses[url]);
}, randomDelay);
}

function output(text) {
console.log(text);
}

// **************************************
// The old-n-busted callback way

function getFile(file) {
fakeAjax(file, function (text) {
fileReceived(file, text);
});
}

function fileReceived(file, text) {
// haven't received this text yet?
if (!responses[file]) {
responses[file] = text;
}

var files = ["file1", "file2", "file3"];

// loop through responses in order for rendering
for (var i = 0; i < files.length; i++) {
// response received?
if (files[i] in responses) {
// response needs to be rendered?
if (responses[files[i]] !== true) {
output(responses[files[i]]);
responses[files[i]] = true;
}
}
// can't render yet
else {
// not complete!
return false;
}
}

output("Complete!");
}

// hold responses in whatever order they come back
var responses = {};

// request all files at once in "parallel"
getFile("file1");
getFile("file2");
getFile("file3");

07 - Callback Problems - Inversion of Control

1
2
3
4
5
6
// line 1
setTimeout(function () {
// line 3
// line 4
}, 1000)
// line 2

We are in control of line1 and line2, line3 and line4 are not in control.

1
2
3
4
5
6
7
trackCheckout(
purchaseInfo,
function finish() {
chargeCreditCard(purchaseInfo)
showThankYouPage()
}
)

Trust

  1. not too early
  2. not too late
  3. not too many times
  4. not too few times
  5. no lost context
  6. no swallowed errors

08 - Callback Problems - Not Reason-able

WRITING ASYNCHRONOUS PROGRAMS IN A SEQUENTIAL MANNER

Example of how we think about code vs how it really works:

1
2
3
4
console.log("First thing happens");
setTimeout(() => {
console.log("Second thing happens");
}, 1000);

That code is essentially expressing in our brains something that works like this:

1
2
3
console.log("First thing happens");
block(1000);
console.log("Second thing happens");

But to the javascript engine, this is what it’s doing:

1
2
3
console.log("First thing happens");
// Do a bunch of other stuff that needs to be done while we wait
console.log("Second thing happens");

09 - Non Fixes

10 - Synchronous and Asynchronous Thunks

Thunks

  • Thunks are an interesting pattern to take time out the equation when trying to access a value.
  • First time you set them up, they do they work, but they don’t give you the value until you pass in the callback.
  • These are the conceptual underpinnings of a Promise.
  • “Thunks are a very lightweight way of solving asynchronous sequencing issues without having to invoke some huge library.”
  • “I don’t need a library. I just need to better understand how to use my currently available tools.”
  • Thunks are about using closure to maintain state.
  • If we can use this tool, a thunk, as a wrapper around a time-based value of state, we can then compose values together without having to worry about the ordering. Time becomes a non-issue.
1
2
3
4
5
6
7
8
9
function add(x, y) {
return x + y
}

var thunk = function () {
return add(10, 15)
}

thunk() // 25

AsyncThunk

1
2
3
4
5
6
7
8
9
10
11
12
13
function addAsync(x, y, cb) {
setTimeout(function () {
cb(x + y)
}, 1000)
}

var thunk = function (cb) {
addAsync(10, 15, cb)
}

thunk(function (sum) {
console.log(sum) // 25
})

Make Thunk (lazy thunk)

1
2
3
4
5
6
7
function makeThunk(fn) {
var args = [].slice.call(arguments, 1)
return function (cb) {
args.push(cb)
fn.apply(null, args)
}
}
1
2
3
4
5
6
7
8
9
10
11
function addAsync(x, y, cb) {
setTimeout(function () {
cb(x + y)
}, 1000)
}

var thunk = makeThunk(addAsync, 10, 15)

thunk(function(sum) {
console.log(sum) // 25
})

What is temporary dependent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function getData(d, cb) {
setTimeout(function () { cb(d); }, 1000);
}

var get10 = makeThunk(getData, 10);
var get30 = makeThunk(getData, 30);

get10(function (num1) {
var x = 1 + num1;
get30(function (num2) {
var y = 1 + num2;

// temporary dependent
var getAnswer = makeThunk(getData,
"Meaning of life: " + (x + y)
);

getAnswer(function (answer) {
console.log(answer);
// Meaning of life: 42
});
});
});

11 - Exercise 2

Instructions

  1. You’ll do the same thing as the previous exercise(s), but now you should use thunks.

  2. Expected behavior:

    • Request all 3 files at the same time (in “parallel”).
    • Render them ASAP (don’t just blindly wait for all to finish loading)
    • BUT, render them in proper (obvious) order: “file1”, “file2”, “file3”.
    • After all 3 are done, output “Complete!”.

code setup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function fakeAjax(url, cb) {
var fake_responses = {
"file1": "The first text",
"file2": "The middle text",
"file3": "The last text"
};
var randomDelay = (Math.round(Math.random() * 1E4) % 8000) + 1000;

console.log("Requesting: " + url);

setTimeout(function () {
cb(fake_responses[url]);
}, randomDelay);
}

function output(text) {
console.log(text);
}

// **************************************

function getFile(file) {
// what do we do here?
}

// request all files at once in "parallel"
// lazy
// Not lazy

12 - Exercise 2 Solution

Notice the active thunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
function fakeAjax(url, cb) {
var fake_responses = {
"file1": "The first text",
"file2": "The middle text",
"file3": "The last text"
};
var randomDelay = (Math.round(Math.random() * 1E4) % 8000) + 1000;

console.log("Requesting: " + url);

setTimeout(function () {
cb(fake_responses[url]);
}, randomDelay);
}

function output(text) {
console.log(text);
}

// **************************************

function getFile(file) {
var resp;

// active thunk, fakeAjax get called immediatly
fakeAjax(file, function (text) {
if (!resp) resp = text;
else resp(text);
});

return function th(cb) {
if (resp) cb(resp);
else resp = cb;
};
}

// request all files at once in "parallel"
var th1 = getFile("file1");
var th2 = getFile("file2");
var th3 = getFile("file3");

th1(function ready(text) {
output(text);
th2(function ready(text) {
output(text);
th3(function ready(text) {
output(text);
output("Complete!");
});
});
});

13 - Thunks and Closure

14 - Native Promises

There is a promises hell, I promise you.

It’s important to understand the concepts behind promises and what they are before you learn the API. This is the reverse of the way things are typically taught. Remember that promises aren’t perfect. When you run into a gotcha, you need to understand the why. There is very much “promises hell”.

Promises allow you to reason about something on the promise of the thing before you have the actual thing itself. They are a codification of the idea that we need a placeholder to eliminate time as a concern wrapped around a value (a future value). They allow you to reason about and write code around a future value, all within the same space, whereas with callbacks you have to make jumps around “this happened here, now in this other file is what happens when you get the value” etc.

Promises are like event listeners. You call the thing and then listen for the outcome. Except in a promise, the listener action is the then() action.

1
2
3
4
5
6
7
// listener
var listener = orderCheeseburger(purchaseInfo);
listener.on('complete', function(){})
listener.on('error', function(){})
// promise
var myPromise = orderCheeseburger(purchaseInfo); // returns a promise
promise.then(function complete(){}, function error(){});

The fundamental design principles of promise are:

  • they only resolved once
  • they result in either a success OR an error
  • messages are passed / kept
  • exceptions become errors
  • they are immutable once resolved (you can send it anywhere, even outside your system, and you know it won’t be changed from under you)

Promises

Future Values

“Completion Events”

15 - Promise API

Async Patterns: “completion event”

1
2
3
4
5
6
7
8
9
10
11
12
function finish () {
...
}

function error(err) {
...
}

var listener = trackCheckout(purchaseInfo)

listener.on("completion", finish)
listener.on("error", error)

Async Patterns: (native) promise

1
2
3
4
5
6
7
8
9
10
function trackCheckout(info) {
return new Promise(
function (resolve, reject) {
// attempt to track the checkout

// if successful, call resolve()
// otherwise, call reject(error)
}
)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function finish () {
...
}

function error(err) {
...
}

var promise = trackCheckout(purchaseInfo)

promise.then(
finish,
error
)

Still callbacks?

Promise Trust

  1. only resolved once
  2. either success OR error
  3. messages passed/kept
  4. exceptions become errors
  5. immutable once resolved

16 - Promise Flow Control

Async Patterns: promise flow control

1
2
3
4
5
doFirstThing
then doSecondThing
then doThirdThing
then complete
or error

Chaining Promises

1
2
3
4
5
6
7
8
9
10
11
doFirstThing()
.then(function () {
return doSecondThing()
})
.then(function () {
return doThirdThing()
})
.then(
complete,
error
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function delay(num) {
return new Promise(function(resolve, reject){
setTimeout(resolve, num)
})
}

delay(100)
.then(function(){
return delay(50)
})
.then(function(){
return delay(200)
})
.then(function(){
console.log("all done!")
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function getData(d) {
return new Promise(function(resolve, reject){
setTimeout(function(){ resolve(d) },1000)
})
}

var x

getData(10)
.then(function(num1) {
x = 1 + num1
return getData(30)
})
.then(function(num2) {
var y = 1 + num2
return getData("Meaning of life: " + (x + y))
})
.then(function(answer) {
console.log(answer)
// Meaning of life: 42
})

17 - Exercise 3

Instructions

  1. You’ll do the same thing as the previous exercise(s), but now you should use promises.

  2. Expected behavior:

    • Request all 3 files at the same time (in “parallel”).
    • Render them ASAP (don’t just blindly wait for all to finish loading)
    • BUT, render them in proper (obvious) order: “file1”, “file2”, “file3”.
    • After all 3 are done, output “Complete!”.

code setup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function fakeAjax(url, cb) {
var fake_responses = {
"file1": "The first text",
"file2": "The middle text",
"file3": "The last text"
};
var randomDelay = (Math.round(Math.random() * 1E4) % 8000) + 1000;

console.log("Requesting: " + url);

setTimeout(function () {
cb(fake_responses[url]);
}, randomDelay);
}

function output(text) {
console.log(text);
}

// **************************************

function getFile(file) {
// what do we do here?
}

// request all files at once in "parallel"
// ???

18 - Exercise 3 Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function fakeAjax(url, cb) {
var fake_responses = {
"file1": "The first text",
"file2": "The middle text",
"file3": "The last text"
};
var randomDelay = (Math.round(Math.random() * 1E4) % 8000) + 1000;

console.log("Requesting: " + url);

setTimeout(function () {
cb(fake_responses[url]);
}, randomDelay);
}

function output(text) {
console.log(text);
}

// **************************************

function getFile(file) {
return new Promise(function (resolve) {
fakeAjax(file, resolve);
});
}

// Request all files at once in
// "parallel" via `getFile(..)`.
var p1 = getFile("file1");
var p2 = getFile("file2");
var p3 = getFile("file3");

// Render as each one finishes,
// but only once previous rendering
// is done.
p1
.then(output)
.then(function () {
return p2;
})
.then(output)
.then(function () {
return p3;
})
.then(output)
.then(function () {
output("Complete!");
});

21 - Exercise 4

code setup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// **************************************
// The old-n-busted callback way

function getFile(file) {
return new Promise(function (resolve) {
fakeAjax(file, resolve);
});
}

// Request all files at once in
// "parallel" via `getFile(..)`.
//
// Render as each one finishes,
// but only once previous rendering
// is done.

// ???

22 - Exercise 4 Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Request all files at once in
// "parallel" via `getFile(..)`.
//
// Render as each one finishes,
// but only once previous rendering
// is done.
["file1", "file2", "file3"]
.map(getFile)
.reduce(
function (chain, filePromise) {
return chain
.then(function () {
return filePromise;
})
.then(output);
},
Promise.resolve() // fulfilled promise to start chain
)
.then(function () {
output("Complete!");
});

23 - Abstractions

Async Patterns: promise “gate”

1
2
3
4
5
6
7
8
9
10
11
12
13
Promise.all([
doTask1a(),
doTask1b(),
doTask1c(),
]).then(function (results) {
return dotask2(
Math.max(
results[0],
results[1],
results[2]
)
)
})

Async Patterns: promise timeout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var p = trySomeAsyncThing()

Promise.race([
p,
new Promise(function (_, reject) {
setTimeout(function() {
reject("Timeout!")
}, 3000)
})
])
.then(
success,
error
)

24 - Sequences and Gates

sequence = automatically chained promises

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ASQ()
.then(function (done) {
setTimeout(done, 1000)
})
.gate(
function (done) {
setTimeout(done, 1000)
},
function (done) {
setTimeout(done, 1000)
}
)
.then(function (done) {
console.log("2 seconds passed!")
})

25 - Exercise 5 and 6

Solve Exercise 3 and 4 using ASQ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// **************************************

function getFile(file) {
return ASQ(function (done) {
fakeAjax(file, done);
});
}

// Request all files at once in
// "parallel" via `getFile(..)`.
//
// Render as each one finishes,
// but only once previous rendering
// is done.
ASQ()
.seq(getFile("file1"))
.val(output)
.seq(getFile("file2"))
.val(output)
.seq(getFile("file3"))
.val(output)
.val(function () {
output("Complete!");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// **************************************

function getFile(file) {
return ASQ(function (done) {
fakeAjax(file, done);
});
}

// Request all files at once in
// "parallel" via `getFile(..)`.
//
// Render as each one finishes,
// but only once previous rendering
// is done.
ASQ()
.seq.apply(null,
["file1", "file2", "file3"]
// Request all files at once in "parallel"
.map(getFile)
// Render output as each file finishes, but
// only once previous rendering is done
.map(function (sq) {
return function () {
return sq.val(output);
};
})
)
.val(function () {
output("Complete!");
});

28 - Generator Example

Generators(yield)

Async Patterns: generators

1
2
3
4
5
6
7
8
9
function* gen() {
console.log("Hello")
yield
console.log("World")
}

var it = gen()
it.next() // Hello
it.next() // World

29 - Messaging

Async Patterns: generators

1
2
3
4
5
6
7
8
9
10
11
12
13
function* main() {
yield 1
yield 2
yield 3
}

var it = main()

it.next() // { value: 1, done: false }
it.next() // { value: 2, done: false }
it.next() // { value: 3, done: false }

it.next() // { value: undefined, done: true }

Async Patterns: generator coroutines

1
2
3
4
5
6
function coroutine(g) {
var it = g()
return function() {
return it.next.apply(it, arguments)
}
}

Async Patterns: generator messages

1
2
3
4
5
6
7
8
9
10
11
12
var run = coroutine(function* () {
var x = 1 + (yield)
var y = 1 + (yield)
yield(x + y)
})

run()

run(10)
console.log(
"Meaning of life: " + run(30).value
);

31 - Async Generators

Async Patterns: yield tasks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getData(d) {
setTimeout(function () {
run(d)
}, 1000)
}

var run = coroutine(function* () {
var x = 1 + (yield getData(10))
var y = 1 + (yield getData(30))
var answer = (yield getData(
"Meaning of life: " + (x + y)
))
console.log(answer)
// Meaning of life: 42
})

run()

32 - Promises and Generators

Async Patterns: generator + sequence tasks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function getData(d) {
return ASQ(function (done) {
setTimeout(function () {
done(d)
}, 1000)
})
}

ASQ()
.runner(function* () {
var x = 1 + (yield getData(10))
var y = 1 + (yield getData(30))
var answer = yield (getData(
"Meaning of life: " + (x + y)
))
yield answer
})
.val(function (answer) {
console.log(answer)
// Meaning of life: 42
})

33 - Exercise 7 ❓

Instructions

  1. You’ll do the same thing as the previous exercise(s), but now you should use asynquence and a generator.

  2. Expected behavior:

    • Request all 3 files at the same time (in “parallel”).
    • Render them ASAP (don’t just blindly wait for all to finish loading)
    • BUT, render them in proper (obvious) order: “file1”, “file2”, “file3”.
    • After all 3 are done, output “Complete!”.

code setup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function fakeAjax(url,cb) {
var fake_responses = {
"file1": "The first text",
"file2": "The middle text",
"file3": "The last text"
};
var randomDelay = (Math.round(Math.random() * 1E4) % 8000) + 1000;

console.log("Requesting: " + url);

setTimeout(function(){
cb(fake_responses[url]);
},randomDelay);
}

function output(text) {
console.log(text);
}

// **************************************

function getFile(file) {
return ASQ(function(done){
fakeAjax(file,done);
});
}

// Request all files at once in
// "parallel" via `getFile(..)`.
//
// Render as each one finishes,
// but only once previous rendering
// is done.

// ???

34 - Exercise 7 Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// **************************************

function getFile(file) {
return ASQ(function (done) {
fakeAjax(file, done);
});
}

// function getFile(file) {
// return new Promise(function(resolve){
// fakeAjax(file,resolve);
// });
// }

ASQ()
.runner(function* loadFiles() {
// Request all files at once in
// "parallel" via `getFile(..)`.
var p1 = getFile("file1");
var p2 = getFile("file2");
var p3 = getFile("file3");

// Render as each one finishes,
// but only once previous rendering
// is done.
output(yield p1);
output(yield p2);
output(yield p3);
})
.val(function () {
output("Complete!");
});

35 - Quiz

1. What is “callback hell”? Why do callbacks suffer from “inversion of control” and “unreasonability”?

  • inversion of control
  • none-local none-sequencial

2. What is a Promise? How does it solve inversion of control issues?

  • future value
  • callback manager

3. How do you pause a generator? How do you resume it?

  • yield
  • next

4. How do we combine generators and promise for flow control?

  • yielding a promise

The Basics Of ES6 Generators

36 - Events and Promises

Concurrency: Event (+ Promise)?

Async Patterns: events + promises

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var p1 = new Promise(function(resolve, reject) {
$('#btn').click(function(evt) {
var className = evt.target.className;
if (/foobar/.test(className)) {
resolve(className)
} else {
reject()
}
})
})

p1.then(function(className) {
console.log(className)
})

problem: promise will only be resolved once.

1
2
3
4
5
6
7
8
9
10
11
12
13
$('#btn').click(function(evt) {
var className = evt.target.className;
var p1 = new Promise(function(resolve, reject) {
if (/foobar/.test(className)) {
resolve(className)
} else {
reject()
}
})
p1.then(function(className) {
console.log(className)
})
})

problem: there is nonsense to use promise.

Async Patterns: events + lists

1
2
3
4
5
6
7
8
9
10
11
12
13
$('#btn').click(function(evt) {

[evt]
.map(function mapper(evt) {
return evt.target.className;
})
.filter(function filterer(className) {
return /foobar/.test(className)
})
.forEach(function (className){
console.log(className)
})
})

37 - Observables

Async Patterns: RxJS observables

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var obsv = Rx.observable.fromEvent(btn, "click");

obsv
.map(function mapper(evt) {
return evt.target.className
})
.filter(function filterer(className) {
return /foobar/.test(className)
})
.distinctUntilChanged()
.subscribe(function (data) {
var className = data[1]
console.log(className)
})

38 - Reactive Sequences

Async Patterns: reactive sequences

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function fromEvent(el, eventType) {
return ASQ.react(function (proceed) {
$(el).bind(eventType, proceed)
})
}

var rsq = fromEvent(btn, 'click')

rsq
.val(function (evt) {
return evt.target.className
})
.then(function (done, className) {
if (/foobar/.test(className)) {
done(className)
}
})
.val(function (className) {
console.log(className)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
var rsq1 = fromEvent(btn, 'click'),
rsq2 = fromEvent(inp, 'keypress'),

rsq3 = ASQ.react.all(rsq1, rsq2),
rsq4 = ASQ.reacr.any(rsq1, rsq2)

rsq3.val(function (ev1, ev2) {
// ..
})

rsq4.val(function (evt) {
// ..
})

39 - Exercise 8

Instructions

  1. In this exercise, we’re going to practice with “reactive sequences” programming (aka “observables”) using asynquence.

  2. Set up a reactive sequence for the stream of click events from the button. Hint: ASQ.react.of() and rsq.push(..)

  3. Set up another reactive sequence that samples the sequence stream from (2) once per second. In other words, if you click the button multiple times in a second, you only get one event message. Hint: setInterval(..)

  4. The sampled sequence should add a “clicked!” message to the list.

code setup

1
2
3
4
5
6
7
8
9
10
$(document).ready(function () {
var $btn = $("#btn"),
$list = $("#list");

$btn.click(function (evt) {
// TODO
});

// TODO: setup sampled sequence, populate $list
});

40 - Exercise 8 Solution Part 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$(document).ready(function () {
var $btn = $("#btn"),
$list = $("#list"),
clicks = ASQ.react.of(),
msgs = ASQ.react.of(),
latest;

$btn.click(function (evt) {
// push click event messages into stream
clicks.push(evt);
});

// sample clicks stream
setInterval(function () {
if (latest) {
msgs.push("clicked!");
latest = null;
}
}, 1000);

// subscribe to click stream
clicks.val(function (evt) {
latest = evt;
});

// subscribe to sampled message stream
msgs.val(function (msg) {
$list.append($("<div>" + msg + "</div>"));
});
});

42 - Concurrency and Channels

CSP: Communicating Sequential Processes

Async Patterns: channel CSP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var ch = chan()

function *process1() {
yield put(ch, "Hello")
var msg = yield take(ch)
console.log(msg)
}

function *process2() {
var greeting = yield take(ch)
yield put(ch, greeting + " World")
console.log("done!")
}

// Hello World
// done!

43 - Blocking Channels

Async Patterns: channel CSP

1
2
3
4
5
6
7
8
9
10
11
12
13
csp.go(function* () {
while(true) {
yield csp.put(ch, Math.random())
}
})

csp.go(function* () {
while(true) {
yield csp.take(csp.timeout(500))
var num = yield csp.take(ch)
console.log(num)
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
csp.go(function* () {
var table = csp.chan()

csp.go(player, ["ping", table])
csp.go(player, ["pong", table])

yield csp.put(table, { hits: 0 })
yield csp.timeout(1000)
table.close()
})

function* player(name, table) {
while(true) {
val ball = yield csp.take(table)
if (ball === csp.CLOSED) {
console.log(name + ": table's gone")
return
}
ball.hits += 1
console.log(name + " " + ball.hits)
yield csp.timeout(100)
yield csp.put(table, ball)
}
}

44 - Event Channels