6th of June, 2015
Asynchronous Javascript
Common and useful patterns
by Steven Rémot
6th of June, 2015
Table of contents
1. Callbacks
2. Promises
3. Generators
6th of June, 2015
Table of contents
1. Callbacks
2. Promises
3. Generators
6th of June, 2015
Callbacks - Usage
function GameController() {
var dirChangedCb = function () {};
this.onDirectionChanged = function (callback) {
dirChangedCb = callback;
};
document.addEventListener('keydown', function (event) {
dirChangedCb(determineEventDirection(event));
});
}
var gc = new GameController();
gc.onDirectionChanged(function (direction) {
hero.move(direction);
});
6th of June, 2015
Callbacks - Avoiding conflicts
function GameController() {
var dirChangedCb = function () {};
this.onDirectionChanged = function (callback) {
dirChangedCb = callback;
};
document.addEventListener('keydown', function (event) {
dirChangedCb(determineEventDirection(event));
});
}
var gc = new GameController();
gc.onDirectionChanged(function (direction) {
hero.move(direction);
});
gc.onDirectionChanged(function (direction) {
enemy.reactTo(direction);
});
Only one callback is saved
6th of June, 2015
Callbacks - Avoiding conflicts
function GameController() {
var dirChangedCbs = [];
this.onDirectionChanged = function (callback) {
dirChangedCbs.push(callback);
};
document.addEventListener('keydown', function (event) {
for (var i = 0; i < dirChangedCbs.length; i++) {
dirChangedCbs[i](determineEventDirection(event));
}
});
}
var gc = new GameController();
gc.onDirectionChanged(function (direction) {
hero.move(direction);
});
gc.onDirectionChanged(function (direction) {
enemy.reactTo(direction);
});
6th of June, 2015
Callbacks - Nested operations
function loadHero(loadingFinishedCallback) {
http.get('/characters/hero.json', function (heroData) {
loadingFinishedCallback({ data: heroData });
});
}
6th of June, 2015
Callbacks - Nested operations
function loadHero(loadingFinishedCallback) {
http.get('/characters/hero.json', function (heroData) {
http.get(heroData.modelUrl, function (model) {
loadingFinishedCallback({
data: heroData,
model: model
});
});
});
}
6th of June, 2015
Callbacks - Nested operations
function loadHero(loadingFinishedCallback) {
http.get('/characters/hero.json', function (heroData) {
http.get(heroData.modelUrl, function (model) {
http.get(model.materialUrl, function (material) {
loadingFinishedCallback({
data: heroData,
model: model,
material: material
});
});
});
});
}
6th of June, 2015
Callbacks - Nested operations
function loadHero(loadingFinishedCallback) {
http.get('/characters/hero.json', function (heroData) {
loadGraphics(heroData.modelUrl, function (graphics) {
loadingFinishedCallback({
data: heroData,
model: graphics.model,
material: graphics.material
});
});
});
}
6th of June, 2015
Callbacks - Limits
● Nested callbacks are not handy
● Even worse with error catching
● Waiting for multiple callbacks is hard
Callbacks are bad for async operations
6th of June, 2015
Table of contents
1. Callbacks
2. Promises
3. Generators
6th of June, 2015
Promises - Basics
function httpGet(url) {
return new Promise(function (resolve) {
http.get(url, function (data) {
resolve(data);
});
});
}
6th of June, 2015
Promises - Basics
function httpGet(url) {
return new Promise(function (resolve) {
http.get(url, resolve);
});
}
6th of June, 2015
Promises - Basics
function httpGet(url) {
return new Promise(function (resolve, reject) {
http.get(url, resolve, reject);
});
}
6th of June, 2015
Promises - Chaining
function loadHero() {
return httpGet('/characters/hero.json')
.then(function (heroData) {
return {
data: heroData
};
});
}
loadHero().then(function (hero) {
console.log(hero.data);
});
6th of June, 2015
Promises - Chaining
function loadHero() {
return httpGet('/characters/hero.json')
.then(function (heroData) {
return httpGet(heroData.modelUrl).then(function (model) {
return {
data: heroData,
model: model
};
});
});
}
loadHero().then(function (hero) {
console.log(hero.data);
console.log(hero.model);
});
6th of June, 2015
Promises - Chaining
function loadHero() {
var hero = {};
return httpGet('/characters/hero.json')
.then(function (heroData) {
hero.data = heroData;
return httpGet(heroData.modelUrl);
})
.then(function (model) {
hero.model = model;
return hero;
});
}
loadHero().then(function (hero) {
console.log(hero.data);
console.log(hero.model);
});
6th of June, 2015
Promises - Chaining
function loadHero() {
var hero = {};
return httpGet('/characters/hero.json')
.then(function (heroData) {
hero.data = heroData;
return httpGet(heroData.modelUrl);
})
.then(function (model) {
hero.model = model;
return httpGet(model.materialUrl);
})
.then(function (material) {
hero.material = material;
return hero;
});
}
6th of June, 2015
Promises - Chaining
function loadHero() {
var hero = {};
return httpGet('/characters/hero.json')
.then(function (heroData) {
hero.data = heroData;
return httpGet(heroData.modelUrl);
})
.then(function (model) {
hero.model = model;
return httpGet(model.materialUrl);
})
.then(function (material) {
hero.material = material;
return hero;
})
.catch(function (error) {
console.log(error);
return null;
});
6th of June, 2015
Promises - Waiting all promises
function loadObjects() {
return Promise.all([
loadHero(),
loadEnemy(),
loadDoor(),
loadWall(),
// ...
]);
}
loadObjects().then(function (objects) {
var hero = objects[0];
var enemy = objects[1];
// ...
});
6th of June, 2015
Promises - How to get them
● In Ecmascript 6 standard library
● Available through polyfills:
https://github.com/jakearchibald/es6-promise
● Alternative APIs:
https://github.com/kriskowal/q
https://github.com/petkaantonov/bluebird
6th of June, 2015
Promises
Nice, but…
● Verbose
● Still writing in an asynchronous way
We can do better!
6th of June, 2015
Table of contents
1. Callbacks
2. Promises
3. Generators
6th of June, 2015
Generator - Basics
function* counter() {
for (var i = 0; i < 10; i++) {
yield i;
}
}
var count = counter();
console.log(count.next()); // 0
console.log(count.next()); // 1
// ...
6th of June, 2015
Generator - Basics
function* loadHero()
{
var hero = {};
hero.data = yield httpGet('/characters/hero.json');
hero.model = yield httpGet(hero.data.modelUrl);
hero.material = yield httpGet(hero.model.materialUrl);
return hero;
}
spawn(loadHero).then(function (hero) {
console.log(hero); // { data: <...>, model: <...>, ... }
});
6th of June, 2015
Generator - Basics
function* loadHero()
{
var hero = {};
try {
hero.data = yield httpGet('/characters/hero.json');
hero.model = yield httpGet(hero.data.modelUrl);
hero.material = yield httpGet(hero.model.materialUrl);
} catch (error) {
console.log(error);
hero = null;
}
return hero;
}
spawn(loadHero).then(function (hero) {
console.log(hero); // { data: <...>, model: <...>, ... }
});
6th of June, 2015
Generator - Basics
async function loadHero()
{
var hero = {};
try {
hero.data = await httpGet('/characters/hero.json');
hero.model = await httpGet(hero.data.modelUrl);
hero.material = await httpGet(hero.model.materialUrl);
} catch (error) {
console.log(error);
hero = null;
}
return hero;
}
loadHero().then(function () {
console.log(hero); // { data: <...>, model: <...>, ... }
});
6th of June, 2015
Generator - Browser support
● Generator are ES6
● Async functions are ES7
● Use them today with transpilers:
https://github.com/google/traceur-compiler
https://babeljs.io/
6th of June, 2015
Thanks for listening!
References:
https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Promise
https://www.promisejs.org/
https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Instructions/function*
https://github.com/lukehoban/ecmascript-asyncawait
6th of June, 2015
Join the community !
(in Paris)
Social networks :
● Follow us on Twitter : https://twitter.com/steamlearn
● Like us on Facebook : https://www.facebook.com/steamlearn
SteamLearn is an Inovia initiative : inovia.fr
You wish to be in the audience ? Join the meetup group!
http://www.meetup.com/Steam-Learn/

Steam Learn: Asynchronous Javascript

  • 1.
    6th of June,2015 Asynchronous Javascript Common and useful patterns by Steven Rémot
  • 2.
    6th of June,2015 Table of contents 1. Callbacks 2. Promises 3. Generators
  • 3.
    6th of June,2015 Table of contents 1. Callbacks 2. Promises 3. Generators
  • 4.
    6th of June,2015 Callbacks - Usage function GameController() { var dirChangedCb = function () {}; this.onDirectionChanged = function (callback) { dirChangedCb = callback; }; document.addEventListener('keydown', function (event) { dirChangedCb(determineEventDirection(event)); }); } var gc = new GameController(); gc.onDirectionChanged(function (direction) { hero.move(direction); });
  • 5.
    6th of June,2015 Callbacks - Avoiding conflicts function GameController() { var dirChangedCb = function () {}; this.onDirectionChanged = function (callback) { dirChangedCb = callback; }; document.addEventListener('keydown', function (event) { dirChangedCb(determineEventDirection(event)); }); } var gc = new GameController(); gc.onDirectionChanged(function (direction) { hero.move(direction); }); gc.onDirectionChanged(function (direction) { enemy.reactTo(direction); }); Only one callback is saved
  • 6.
    6th of June,2015 Callbacks - Avoiding conflicts function GameController() { var dirChangedCbs = []; this.onDirectionChanged = function (callback) { dirChangedCbs.push(callback); }; document.addEventListener('keydown', function (event) { for (var i = 0; i < dirChangedCbs.length; i++) { dirChangedCbs[i](determineEventDirection(event)); } }); } var gc = new GameController(); gc.onDirectionChanged(function (direction) { hero.move(direction); }); gc.onDirectionChanged(function (direction) { enemy.reactTo(direction); });
  • 7.
    6th of June,2015 Callbacks - Nested operations function loadHero(loadingFinishedCallback) { http.get('/characters/hero.json', function (heroData) { loadingFinishedCallback({ data: heroData }); }); }
  • 8.
    6th of June,2015 Callbacks - Nested operations function loadHero(loadingFinishedCallback) { http.get('/characters/hero.json', function (heroData) { http.get(heroData.modelUrl, function (model) { loadingFinishedCallback({ data: heroData, model: model }); }); }); }
  • 9.
    6th of June,2015 Callbacks - Nested operations function loadHero(loadingFinishedCallback) { http.get('/characters/hero.json', function (heroData) { http.get(heroData.modelUrl, function (model) { http.get(model.materialUrl, function (material) { loadingFinishedCallback({ data: heroData, model: model, material: material }); }); }); }); }
  • 10.
    6th of June,2015 Callbacks - Nested operations function loadHero(loadingFinishedCallback) { http.get('/characters/hero.json', function (heroData) { loadGraphics(heroData.modelUrl, function (graphics) { loadingFinishedCallback({ data: heroData, model: graphics.model, material: graphics.material }); }); }); }
  • 11.
    6th of June,2015 Callbacks - Limits ● Nested callbacks are not handy ● Even worse with error catching ● Waiting for multiple callbacks is hard Callbacks are bad for async operations
  • 12.
    6th of June,2015 Table of contents 1. Callbacks 2. Promises 3. Generators
  • 13.
    6th of June,2015 Promises - Basics function httpGet(url) { return new Promise(function (resolve) { http.get(url, function (data) { resolve(data); }); }); }
  • 14.
    6th of June,2015 Promises - Basics function httpGet(url) { return new Promise(function (resolve) { http.get(url, resolve); }); }
  • 15.
    6th of June,2015 Promises - Basics function httpGet(url) { return new Promise(function (resolve, reject) { http.get(url, resolve, reject); }); }
  • 16.
    6th of June,2015 Promises - Chaining function loadHero() { return httpGet('/characters/hero.json') .then(function (heroData) { return { data: heroData }; }); } loadHero().then(function (hero) { console.log(hero.data); });
  • 17.
    6th of June,2015 Promises - Chaining function loadHero() { return httpGet('/characters/hero.json') .then(function (heroData) { return httpGet(heroData.modelUrl).then(function (model) { return { data: heroData, model: model }; }); }); } loadHero().then(function (hero) { console.log(hero.data); console.log(hero.model); });
  • 18.
    6th of June,2015 Promises - Chaining function loadHero() { var hero = {}; return httpGet('/characters/hero.json') .then(function (heroData) { hero.data = heroData; return httpGet(heroData.modelUrl); }) .then(function (model) { hero.model = model; return hero; }); } loadHero().then(function (hero) { console.log(hero.data); console.log(hero.model); });
  • 19.
    6th of June,2015 Promises - Chaining function loadHero() { var hero = {}; return httpGet('/characters/hero.json') .then(function (heroData) { hero.data = heroData; return httpGet(heroData.modelUrl); }) .then(function (model) { hero.model = model; return httpGet(model.materialUrl); }) .then(function (material) { hero.material = material; return hero; }); }
  • 20.
    6th of June,2015 Promises - Chaining function loadHero() { var hero = {}; return httpGet('/characters/hero.json') .then(function (heroData) { hero.data = heroData; return httpGet(heroData.modelUrl); }) .then(function (model) { hero.model = model; return httpGet(model.materialUrl); }) .then(function (material) { hero.material = material; return hero; }) .catch(function (error) { console.log(error); return null; });
  • 21.
    6th of June,2015 Promises - Waiting all promises function loadObjects() { return Promise.all([ loadHero(), loadEnemy(), loadDoor(), loadWall(), // ... ]); } loadObjects().then(function (objects) { var hero = objects[0]; var enemy = objects[1]; // ... });
  • 22.
    6th of June,2015 Promises - How to get them ● In Ecmascript 6 standard library ● Available through polyfills: https://github.com/jakearchibald/es6-promise ● Alternative APIs: https://github.com/kriskowal/q https://github.com/petkaantonov/bluebird
  • 23.
    6th of June,2015 Promises Nice, but… ● Verbose ● Still writing in an asynchronous way We can do better!
  • 24.
    6th of June,2015 Table of contents 1. Callbacks 2. Promises 3. Generators
  • 25.
    6th of June,2015 Generator - Basics function* counter() { for (var i = 0; i < 10; i++) { yield i; } } var count = counter(); console.log(count.next()); // 0 console.log(count.next()); // 1 // ...
  • 26.
    6th of June,2015 Generator - Basics function* loadHero() { var hero = {}; hero.data = yield httpGet('/characters/hero.json'); hero.model = yield httpGet(hero.data.modelUrl); hero.material = yield httpGet(hero.model.materialUrl); return hero; } spawn(loadHero).then(function (hero) { console.log(hero); // { data: <...>, model: <...>, ... } });
  • 27.
    6th of June,2015 Generator - Basics function* loadHero() { var hero = {}; try { hero.data = yield httpGet('/characters/hero.json'); hero.model = yield httpGet(hero.data.modelUrl); hero.material = yield httpGet(hero.model.materialUrl); } catch (error) { console.log(error); hero = null; } return hero; } spawn(loadHero).then(function (hero) { console.log(hero); // { data: <...>, model: <...>, ... } });
  • 28.
    6th of June,2015 Generator - Basics async function loadHero() { var hero = {}; try { hero.data = await httpGet('/characters/hero.json'); hero.model = await httpGet(hero.data.modelUrl); hero.material = await httpGet(hero.model.materialUrl); } catch (error) { console.log(error); hero = null; } return hero; } loadHero().then(function () { console.log(hero); // { data: <...>, model: <...>, ... } });
  • 29.
    6th of June,2015 Generator - Browser support ● Generator are ES6 ● Async functions are ES7 ● Use them today with transpilers: https://github.com/google/traceur-compiler https://babeljs.io/
  • 30.
    6th of June,2015 Thanks for listening! References: https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Promise https://www.promisejs.org/ https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Instructions/function* https://github.com/lukehoban/ecmascript-asyncawait
  • 31.
    6th of June,2015 Join the community ! (in Paris) Social networks : ● Follow us on Twitter : https://twitter.com/steamlearn ● Like us on Facebook : https://www.facebook.com/steamlearn SteamLearn is an Inovia initiative : inovia.fr You wish to be in the audience ? Join the meetup group! http://www.meetup.com/Steam-Learn/