The Story of 3 files
JS Async Patterns
http://bit.ly/js-async
Salama Ashoush
Software Developer at Orange Labs
Part-time Front-end developer at Pushbots
ITI OSAD
I love Anime and Javascript
I don't like java
salamaashoush@gmail.com
@salamaashoush

Once upon a time ...
A poor web developer wants to make a three request in parallel with javascript, he thinks a little and said to himself well it is very easy, i can spawn three threads like I do every day with java
he opened google and start typing how to spawn three threads with JavaScript and .....

he started to wonder how things work in the browser with a single thread?
Dom Events ????
Ajax????
etc???
and after a long day of searching and reading, he finally understood that ...
Js has non-blocking io async model with event loop??
https://vimeo.com/96425312
he said, now I can do the job, and he opened google and searched for "how to write async code in js"?
and he found ...
7 Async Patterns
Agenda
- Concurrency
- Callbacks
- Thunks
- Promises
- Generators
- Aysnc await
- Observable *
- CSP *
Callbacks
function fakeAjax(param1,param2,..,cb){
....
cb('result');
}Implementation
let req1 = fakeAjax('/fake',(result)=>{
console.log(result);
});Usage
Pyramid of doom/Callback Hell
setTimeout(()=>{
console.log('one');
setTimeout(()=>{
console.log('two');
setTimeout(()=>{
console.log('three');
},1000)
},1000)
},1000)// continuation passing style
function one(cb){
console.log('one');
setTimeout(cb,1000);
}
function two(cb){
console.log('two');
setTimeout(cb,1000);
}
function three(){
console.log('three')
}
one(()=>two(three()));Inversion of control
trackCheckout(purchaseInfo,()=>{
chargeCreditCard(purchaseInfo);
showThankyouPage();
});let called = false;
trackCheckout(purchaseInfo,()=>{
if(!called){
called = true;
chargeCreditCard(purchaseInfo);
showThankyouPage();
}
});Trust:
- not too early
- not too late
- not too many times
- not too few times
- no lost context
- no swallowed errors
- .....
You have to fix all of them
callback shapes
function doSomething(ok,err){
//...
if(error){
err(error);
}else{
ok(result);
}
}
doSomething((result)=>{
console.log(result);
},(error)=>{
console.log(error);
})function doSomething(cb){
//...
if(error){
cb(error);
}else{
cb(null,result);
}
}
doSomething((error,result)=>{
if(error){
console.log(error);
}else{
return console.log(result);
}
});Separate callbacks:
Error first callbacks (Node):
3 parallel requests problem:
we need to make three requests in parallel and output them as soon as possible in order
"file1","file2","file3"
function fakeAjax(url,cb) {
let fake_responses = {
"file1": "The first text",
"file2": "The middle text",
"file3": "The last text"
};
let randomDelay = (Math.round(Math.random() * 1E4) % 8000) + 1000;
console.log("Requesting: " + url);
setTimeout(function(){
cb(fake_responses[url]);
},randomDelay);
}// hold responses in whatever order they come back
let responses = {};
function getFile(file) {
fakeAjax(file,(text) => {
fileReceived(file,text);
});
}
function fileReceived(file,text) {
// haven't received this text yet?
if (!responses[file]) {
responses[file] = text;
}
let files = ["file1","file2","file3"];
// loop through responses in order for rendering
for (let i=0; i<files.length; i++) {
// response received?
if (files[i] in responses) {
// response needs to be rendered?
if (responses[files[i]] !== true) {
console.log(responses[files[i]]);
responses[files[i]] = true;
}
} else {
// can't render yet
// not complete!
return false;
}
}
console.log("Complete!");
}
// request all files at once in "parallel"
getFile("file1");
getFile("file2");
getFile("file3");Thunks
a function that encapsulates synchronous or asynchronous code inside.
function add(x,y){
return x + y;
}
let thunk = () => {
add(5,6);
}
// always returns the same value 11
thunk();sync thunk
function addAsync(x,y,cb){
setTimeout(()=>cb(x+y),100);
}
let thunk = (cb) => {
addAsync(5,6,cb);
}
// it only need a callback
// to do the work
thunk((sum)=>{
console.log(sum);
});async thunk
a container around a particular collection of state, and you can use it everywhere to extract the same state
time-independent wrapper around the value
Closures
+
thunks
===
power
// active thunk
function getFile(file) {
let resp;
fakeAjax(file,function(text){
if (!resp) resp = text;
else resp(text);
});
return (cb) => {
if (resp) cb(resp);
else resp = cb;
};
}
// request all files at once in "parallel"
let th1 = getFile("file1");
let th2 = getFile("file2");
let th3 = getFile("file3");
th1((text) => {
console.log(text);
th2((text) => {
console.log(text);
th3((text) => {
console.log(text);
console.log("Complete!");
});
});
});thunks eliminated the when part of the equation
more info about callbacks and thunks
- "Rethinking async programming in js" kyle course on frontend masters
- "you don't know js (async/performance book)"
Promises
I promise you nothing
Restaurant metaphor
again a time-independent wrapper around the value
sounds familiar
Future values
"Completion/Error Events"
imagine that it returns an event listener and you listen to "complete" or "error" event
function getFile(file){
return new Promise((resolve, reject) => {
fakeAjax(file,(error, results) => {
if(error) return reject(error);
return resolve(results);
});
});
}Implementation
getFile('file1').then((file)=>{
console.log(file);
}).catch((error)=>{
console.log(error);
}).finally(()=>{
console.log('completed');
})Usage
- Pending, when the final value is not available yet. This is the only state that may transition to one of the other two states.
- Fulfilled, when and if the final value becomes available. A fulfillment value becomes permanently associated with the promise. This may be any value, including undefined.
- Rejected, if an error prevented the final value from being determined. A rejection reason becomes permanently associated with the promise. This may be any value, including undefined, though it is generally an Error object, like in exception handling.
- Settled, when the promise is completely finished his work
Promise status
Promises Guarantees
-
Callbacks will never be called before the completion of the current run of the JavaScript event loop.
-
Callbacks added with .then even after the success or failure of the asynchronous operation will be called
-
Multiple callbacks may be added by calling .then several times, to be executed independently in insertion order.
Promise Chaining
Chain then after catch
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);doSomething()
.then(() => {
throw new Error('Something failed');
console.log('Do this');
})
.catch(() => {
console.log('Do that');
})
.then(() => {
console.log('Do this whatever happened before');
});Composition
//shortcuts to manually create an already resolved or rejected promise respectively. This can be useful at times.
Promise.resolve();
Promise.reject();
//two composition tools for running asynchronous operations in parallel.
Promise.all([p1,p2]);
Promise.race([p1,p2]);
//Sequential composition is possible using some clever JavaScript:
[func1, func2].reduce((p, f) => p.then(f), Promise.resolve());
function getFile(file) {
return new Promise((resolve) => {
fakeAjax(file,resolve);
});
}
// Request all files at once in
// "parallel" via `getFile(..)`.
let p1 = getFile("file1");
let p2 = getFile("file2");
let p3 = getFile("file3");
// Render as each one finishes,
// but only once previous rendering
// is done.
p1
.then(console.log)
.then(() => p2)
.then(console.log)
.then(() => p3)
.then(console.log)
.then(() => console.log("Complete!"));
// or
["file1","file2","file3"]
.map(getFile)
.reduce((chain, p) => chain.then(()=>p).then(console.log), Promise.resolve());Generators
the first time to understand generators
function* gen() {
yield 1;
yield 2;
yield 3;
}
let g = gen(); // iterator object
g.next(); // {value: 1, done: false}
g.next(); // {value: 2, done: false}
g.next(); // {value: 3, done: false}
g.next(); // {value: undefined, done: true}
// for of loop
for(let i of g){
console.log(value)
}Run to completion semantics
the fact that whenever a function runs, it cannot be pre-empted and will run entirely before any other code runs
Generator is special
unlike normal function generator function can pause and continue execution in a different time and it i could run for ever without complete
function* idMaker() {
var index = 0;
while(true)
yield index++;
}
let gen = idMaker(); // "Generator { }"
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
// ... after many lines of code
//...
console.log(gen.next().value); // 3Push
(Observer) --> producer in control
Dom Events
WebSockets
SereverSentEvents
NodeStreams
ServiceWorker
setInterval
Pull
(Iterator) --> consumer in controll
Arrays
etc
Generators let you write code looks like you push a value and it converts it to pull
https://www.youtube.com/watch?v=lil4YCCXRYc&t=1382s
function* logGenerator() {
console.log(0);
console.log(1, yield);
console.log(2, yield);
console.log(3, yield);
}
let gen = logGenerator();
gen.next(); // 0
gen.next('pretzel'); // 1 pretzel
gen.next('california'); // 2 california
gen.next('mayonnaise'); // 3 mayonnaiseGenerator Object
- Generator.next();
- Generator.throw();
- Generator.return();
// return will complete the generator
function* gen() {
yield 1;
return 5;
yield 2; // unreachable code
}

Why does he mention this thing with async patterns?
Async Generators
function getData(v){
setTimeout(()=>g.next(v),1000)
}
function * gen(){
try{
let x = yield getData(5);
let y = x + (yield getData(6));
console.log(y); // 11
}catch(err){
console.log(err)
}
}
let g = gen();
g.next();Generators + Promises
co(function *(){
// resolve multiple promises in parallel
var a = Promise.resolve(1);
var b = Promise.resolve(2);
var c = Promise.resolve(3);
var res = yield [a, b, c];
console.log(res);
// => [1, 2, 3]
}).catch(onerror);
//find more here https://github.com/tj/cofunction getFile(file) {
return new Promise(function(resolve){
fakeAjax(file,resolve);
});
}
co(function *loadFiles(){
// Request all files at once in
// "parallel" via `getFile(..)`.
let p1 = getFile("file1");
let p2 = getFile("file2");
let p3 = getFile("file3");
// Render as each one finishes,
// but only once previous rendering
// is done.
try{
console.log( yield p1 );
console.log( yield p2 );
console.log( yield p3 );
}catch(error){
console.log(error)
}
})
.then(() => console.log("Complete!"))When I finally understood the power of generators
Async Await
Beauty of writing sync-looking async code

function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function add1(x) {
try{
const a = await resolveAfter2Seconds(20);
const b = await resolveAfter2Seconds(30);
return x + a + b;
}catch(err){
console.log(err)
}
}
add1(10).then(v => {
console.log(v); // prints 60 after 4 seconds.
})function getFile(file) {
return new Promise((resolve) => {
fakeAjax(file,resolve);
});
}
(async () => {
// Request all files at once in
// "parallel" via `getFile(..)`.
let p1 = getFile("file1");
let p2 = getFile("file2");
let p3 = getFile("file3");
// Render as each one finishes,
// but only once previous rendering
// is done.
try{
console.log( await p1 );
console.log( await p2 );
console.log( await p3 );
}catch(error){
console.log(error);
}
})()
.then(() => console.log("Complete!"))// Asynchronous generator
async function* asyncGen() {
yield 'a';
yield 'b';
}
const iter = asyncGen()[Symbol.asyncIterator]();
iter.next().then(x => console.log(x));
// { value: 'a', done: false }
iter.next().then(x => console.log(x));
// { value: 'b', done: false }
iter.next().then(x => console.log(x));
// { value: undefined, done: true }Asynchronous iterables return asynchronous iterators, whose method next()returns Promises for {value, done} objects:
Asynchronous iterables
//sync for of loop with await inside async function
for (const x of await Promise.all([p1,p2,p3]));
// for await is exactly the same
for await (const x of [p1,p2,p3]);
// in action
// note: you can use for-await-of inside async functions only
async function main() {
const syncIterable = [
Promise.resolve('a'),
Promise.resolve('b'),
];
for await (const x of syncIterable) {
console.log(x);
}
}
main();for-await-of
async function* readLines(path) {
let file = await fileOpen(path);
try {
while (!file.EOF) {
yield await file.readLine();
}
} finally {
await file.close();
}
}
for await (const line of readLines(filePath)) {
console.log(line);
}Observables
Event streams
Push
observable pushes the value to the consumer
interface iterable {
Generator iterator(void)
}
interface observable {
void observer(Generator)
}
https://www.youtube.com/watch?v=lil4YCCXRYc&t=1382s
function listen(element, eventName) {
return new Observable(observer => {
// Create an event handler which sends data to the sink
let handler = event => observer.next(event);
// Attach the event handler
element.addEventListener(eventName, handler, true);
// Return a cleanup function which will cancel the event stream
return () => {
// Detach the event handler from the element
element.removeEventListener(eventName, handler, true);
};
});
}
// Return an observable of special key down commands
function commandKeys(element) {
let keyCommands = { "38": "up", "40": "down" };
return listen(element, "keydown")
.filter(event => event.keyCode in keyCommands)
.map(event => keyCommands[event.keyCode])
}
//subscribe to the observable
let subscription = commandKeys(inputElement).subscribe({
next(val) { console.log("Received key command: " + val) },
error(err) { console.log("Received an error: " + err) },
complete() { console.log("Stream complete") },
});
// After calling this function, no more events will be sent
subscription.unsubscribe();Proposed Observable interface
//creates an Observable of the values provided as arguments
Observable.of("red", "green", "blue").subscribe({
next(color) {
console.log(color);
}
});
/*
> "red"
> "green"
> "blue"
*///Converting from an iterable to an Observable:
Observable.from(["mercury", "venus", "earth"]).subscribe({
next(value) {
console.log(value);
}
});
/*
> "mercury"
> "venus"
> "earth"
*/https://github.com/tc39/proposal-observable
Working ES Observable implementations
observable proposal on stage1
CSP
Communicating sequential processes
CSP is a way to manage concurrency via channels
one of the reasons behind go-lang performance is its concurrency modal via channels
GOROUTINES
put and take values from channels is blocking by default
automatic back-pressure (streams concept)
let ch = chan();
function *process1(){
yield put(ch,"Hello");
let msg = yield take(ch);
console.log(msg);
}
function *proccess2(){
let greeting = yield take(ch);
yield put(ch,`${greeting} world`);
console.log("done!");
}Channels concepts
The Process
The Process Can Pause
Processes Wait For Values In Channels
Processes Communicate Through Channels
Channel Are Queues
http://lucasmreis.github.io/blog/quick-introduction-to-csp-in-javascript/
let ch = csp.chan();
csp.go(function*(){
while(true){
yield csp.put(ch, Math.random());
}
});
csp.go(function*(){
while(true){
yield csp.take(csp.timeout(500));
let num = yield csp.take(ch);
console.log(num);
}
})Js csp
https://github.com/ubolonton/js-csp
function fromEvent(el,eventType){
let ch = csp.chan();
document.addEventListener(el,(e)=>{
csp.putAsync(ch,e)
});
return ch;
}
csp.go(function*(){
let ch = fromEvent(el, 'mousemove');
while(true){
let event = yield csp.put(ch, Math.random());
console.log(`${event.clientX}, event.clientY`);
}
});
Event listener with channels
function* player(name, table) {
while (true) {
let 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);
}
}
csp.go(function* () {
const 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();
});Ping/Pong
Js Async programming is awesome
?
Js Async Patterns
By Salama Ashoush
Js Async Patterns
- 188