Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Promises, generators, async/await!!

Promises, generators, async/await!!

Lo standard EcmaScript si sta finalmente evolvendo ad un ritmo mantenuto.. ES6 ed ES7 hanno in serbo per noi un sacco di novità, la più interessante e rivoluzionaria delle quali è sicuramente un nuovo, interessante e promettente approccio all'asincronicità. In questo talk ripercorreremo un po' la storia delle soluzioni storiche adottate per risolvere l'annosa problematica del "callback hell", per arrivare alla soluzione definitiva!

Stefano Verna

February 24, 2016
Tweet

More Decks by Stefano Verna

Other Decks in Programming

Transcript

  1. CALLBACKS A piece of executable code that is passed as

    an argument to other code, which is expected to call back (execute) the argument at some convenient time.
  2. CALLBACK HELL router.get('/dashboard', function(req, res) { Stats.getMemoryUsage(function(err, memory) { Stats.getCPUUsage(function(err,

    cpu) { Stats.getUserRetention(function(err, retention) { res.render('dashboard', { memory: memory, cpu: cpu, retention: retention }); }); }); }); });
  3. CALLBACK HELL /2 getData(function(a) { getMoreData(a, function(b) { getMoreData(b, function(c)

    { getMoreData(c, function(d) { getMoreData(d, function(e) { // ... }); }); }); }); });
  4. CALLBACK HELL /3 getData(function(err, a) { if (err) { handleError(err);

    } else { getMoreData(a, function(err, b) { if (err) { handleError(err); } else { getMoreData(b, function(err, c) { // ... }); } }); } });
  5. !

  6. SO, WE DON'T BLOCK BUT... > No return/catch > Callback

    hell > Callback functions tend to become difficult to maintain and debug when nested within long lines of code > Anonymous inline functions in a callback can make reading the call stack very tedious
  7. async.map(['file1', 'file2', 'file3'], fs.stat, function(err, results){ // results is now

    an array of stats for each file }); async.filter(['file1', 'file2', 'file3'], fs.exists, function(results){ // results now equals an array of the existing files });
  8. async.parallel([ function(cb) { setTimeout(function() { cb(null, 'one'); }, 1000); },

    function(cb) { setTimeout(function() { cb(null, 'two'); }, 2000); } ], function(err, results){ // the results array will equal ['one', 'two'] }); async.series([ function(cb) { fs.exists('file1', cb); }, function(cb) { fs.exists('file2', cb); } ], function(err, results){ // the results array will equal [true, false] });
  9. async.waterfall([ function(callback) { callback(null, 'one', 'two'); }, function(arg1, arg2, callback)

    { // arg1 now equals 'one' and arg2 now equals 'two' callback(null, 'three'); }, function(arg1, callback) { // arg1 now equals 'three' callback(null, 'done'); } ], function (err, result) { // result now equals 'done' });
  10. var promise = new Promise(function(resolve, reject) { // do your

    async stuff resolve("Done!"); }); promise .then(function(result) { // result now equals "Done!" }); promise .then(function(result) { // also here it equals "Done!" });
  11. var promise = new Promise(function(resolve, reject) { // do your

    async stuff reject(new Error("Fail!")); }); promise .then(function(result) { // does not get called }) .catch(function(error) { // this does! });
  12. function sleepAndReturn(duration, value) { return new Promise(function(resolve, reject) { setTimeout(

    function() { resolve(value); }, duration ); }); } sleepAndReturn(1000, "Done!") .then(function(result) { // result now equals "Done!" })
  13. PROMISE CHAINING sleepAndReturn(1000, "Done!") .then(function(result1) { return result1 + "!!!!";

    }) .then(function(result2) { // result2 now equals "Done!!!!!" });
  14. PROMISE CHAINING /2 sleepAndReturn(1000, "Done!") .then(function(result1) { return sleepAndReturn(1000, result1

    + "!!!!"); }) .then(function(result2) { // result2 now equals "Done!!!!!" });
  15. PROMISE CHAINING /3 sleepAndReturn(1000, "Done!") .then(function(result1) { throw new Error("Fuck..");

    }) .then(function(result2) { // ... }) .then(function(result2) { // ... }) .catch(function(error) { console.error(error.message); });
  16. FROM THIS... getData(function(a) { getMoreData(a, function(b) { getMoreData(b, function(c) {

    getMoreData(c, function(d) { getMoreData(d, function(e) { // ... }); }); }); }); });
  17. RECAP > It is easier to read as in cleaner

    method signatures > It allows us to attach more than one callback to a single promise > It allows for chaining of promises
  18. IT'S STILL QUITE CUMBERSOME TO HANDLE EVEN SIMPLE LOGIC THOUGH...

    JUST THINK ABOUT MAKING A FOR-LOOP WITH PROMISES...
  19. var chunks = ["foo", "bar", "baz"]; var promiseChain = chunks.reduce(function(acc,

    chunk) { return acc.then(function(result) { return result + chunk; }); }, Promise.resolve("")); promiseChain.then(function(result) { console.log(result); });
  20. !

  21. function makeIterator() { var i = 0; return { next:

    function() { return i < 3 ? { value: i++, done: false } : { done: true }; } } } var iterator = makeIterator(); console.log(iterator.next().value); // 0 console.log(iterator.next().value); // 1 console.log(iterator.next().value); // 2 console.log(iterator.next().done); // true
  22. var someCustomObject = {}; someCustomObject[Symbol.iterator] = makeIterator; for (var i

    of someCustomObject) { console.log(i); } // 0 // 1 // 2
  23. A generator is a special type of function that works

    as a factory for iterators. A FUNCTION BECOMES A GENERATOR IF IT CONTAINS ONE OR MORE yield EXPRESSIONS AND IF IT USES THE function* SYNTAX.
  24. function *makeIterator() { for (var i=0; i<3; i++) { yield

    i; } }; var iterator = makeIterator(); console.log(iterator.next().value); // 0 console.log(iterator.next().value); // 1 console.log(iterator.next().value); // 2 console.log(iterator.next().done); // true
  25. Generators compute their yielded values on demand, which allows them

    to efficiently represent sequences that are expensive to compute, or even infinite sequences!
  26. function *count() { var i = 0; while (true) {

    yield i++; } }; var iterator = count(); console.log(iterator.next().value); // 0 console.log(iterator.next().value); // 1 console.log(iterator.next().value); // 2 // ...
  27. PLOT TWIST YIELD CAN READ VALUES PASSED FROM OUTSIDE! THE

    NEXT() METHOD ACCEPTS A VALUE WHICH CAN BE USED TO MODIFY THE INTERNAL STATE OF THE GENERATOR. A VALUE PASSED TO NEXT() WILL BE TREATED AS THE RESULT OF THE LAST YIELD EXPRESSION THAT PAUSED THE GENERATOR.
  28. function *count() { var i = 0; while (true) {

    var clear = yield i++; if (clear) { i = 0; } } }; var iterator = count(); console.log(iterator.next().value); // 0 console.log(iterator.next().value); // 1 console.log(iterator.next(true).value); // 0 console.log(iterator.next().value); // 1 console.log(iterator.next().value); // 2 // ...
  29. WAIT A MINUTE... INSIDE GENERATORS EXECUTION OF CODE CAN BE

    PAUSED! THAT IS, EXECUTION RESUMES WHEN THE EXTERNAL WORLD CALLS NEXT() ANOTHER TIME
  30. function request(url) { makeAjaxCall(url, function(response){ it.next(response); }); } function *main()

    { var result1 = yield request("http://some.url.1"); var data = JSON.parse(result1); var result2 = yield request("http://some.url.2?id=" + data.id); var resp = JSON.parse(result2); console.log("The value you asked for: " + resp.value); } var it = main(); it.next(); // get it all started
  31. A coroutine is a function, marked by the function* syntax,

    which can suspend itself anywhere in its execution using the yield keyword.
  32. function nirvana(g) { var it = g(), ret; (function iterate(val)

    { ret = it.next(val); if (!ret.done) { ret.value.then(iterate); } })(); }
  33. nirvana(function *(){ var searchTerms = yield Promise.all([ request("http://some.url.1"), request("http://some.url.2"), request("http://some.url.3")

    ]); var searchResults = yield request( "http://some.url.4?search=" + searchTerms.join("+") ); var resp = JSON.parse(searchResults); console.log("Search results: " + resp.value); });
  34. var koa = require('koa'); var route = require('koa-route'); var app

    = koa(); app.use(route.get('/posts', function *() { var posts = yield fetchAllPosts(); this.body = "There are " + posts.length + "posts in DB!"; })); app.listen(3000);
  35. An async function is a special type of function that

    works as a factory for promises.
  36. async function makePromise() { return "bar"; } var promise =

    makePromise(); promise.then((value) => { // value equals "bar" });
  37. async function makePromise() { throw new Error("Whoops!"); } var promise

    = makePromise(); promise.catch((error) => { // error.message equals "Whoops!" });
  38. function sleep(duration) { return new Promise(function(resolve, reject) { setTimeout(function() {

    resolve() }, duration); }); } function request(url) { return new Promise(function(resolve, reject) { makeAjaxCall(url, function(err, text) { err ? reject(err) : resolve(text); }); }); }
  39. async function sleepAndRequest(duration, url) { try { await sleep(duration); var

    result = await request(url); return result; } catch(err) { // some promise was rejected! throw err; } }
  40. NODE.JS > Generators are already available in Node.js since v0.11

    > To use async/await you need a transpiler (Babel) BROWSERS > You need to transpile in any case :)
  41. THE CHERRY ON THE CAKE HOW THE HELL BABEL CAN

    GENERATE ES5 CODE ABLE TO REPLICATE THE FEATURES OF GENERATOR/ASYNC FUNCTIONS?!
  42. async function foo() { await sleep(100); return await request("http://foo.bar"); }

    function foo() { return regeneratorRuntime.async(function foo$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: context$1$0.next = 2; return regeneratorRuntime.awrap(sleep(100)); case 2: context$1$0.next = 4; return regeneratorRuntime.awrap(request("http://foo.bar")); case 4: return context$1$0.abrupt("return", context$1$0.sent); case 5: case "end": return context$1$0.stop(); } }, null, this); }
  43. !

  44. That's all I got... ! THANKS FOR LISTENING! ! >

    Gihtub stefanoverna > Twitter steffoz