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

Async JavaScript 2016

Async JavaScript 2016

Anton Medvedev

March 17, 2016
Tweet

Other Decks in Programming

Transcript

  1. Blocking code var response = request('https://api.github.com/repos/v8/v8/contributors'); var contributors = parseResponse(response);

    var success = request('https://hooks.slack.com/...', { payload: {text: "Top ten contributors: " + contributors.join(',')} }); Everyone knows why the blocking code is bad: it’s stops JavaScript execution, freezes UI. But this code is also good: it simple, intuitive, and maintainable.
  2. Callback hell // ... $.getJSON('/api/...', function () { // We

    made it! }); }); }); }); }); }); }); }); }); Use of callbacks leads to callback hell.
  3. Promise fetch('https://api.github.com/repos/v8/v8/contributors') .then(function (response) { return response.json(); }) .then(function (json)

    { var contributors = parseResponse(json); return fetch('https://hooks.slack.com/...', {…}); }) .then(function (response) { if (response.status == 200) { console.log('Success'); } else { throw "Error"; } }) .catch(function(error) { console.log('Request failed:', error); }); Promises to the rescue! …but we can do better
  4. ES2015 Generators function* () { yield 1; yield 2; yield

    3; } Generators are functions which can be exited and later re-entered.
  5. Generators function* numbers() { yield 1; yield 2; yield 3;

    return 4; } > var iterator = numbers(); > iterator.next(); < {value: 1, done: false} > iterator.next(); < {value: 2, done: false} > iterator.next(); < {value: 3, done: false} > iterator.next(); < {value: 4, done: true} Calling a generator function does not execute its body immediately; an iterator object for the function is returned instead.
  6. Generators function* fibonacci() { var n1 = 1, n2 =

    1, sum; yield n1; yield n2; while (true) { sum = n1 + n2; n1 = n2; n2 = sum; yield sum; } } function fibonacci() { var state = 0; return { next: function () { var n1 = 1, n2 = 1, sum; switch (state) { case 0: state = 1; return {value: n1, done: false}; case 1: state = 2; return {value: n2, done: false}; case 2: sum = n1 + n2; n1 = n2; n2 = sum; return {value: sum, done: false}; } } }; } In internal representation generator is a finite state machines.
  7. Generators function* generator() { var value1 = yield 1; var

    value2 = yield 2; return value1 * value2; } > var iterator = generator(); > iterator.next(); < {value: 1, done: false} > iterator.next(2); < {value: 2, done: false} > iterator.next(3); < {value: 6, done: true} The next() method also accepts a value which can be used to modify the internal state of the generator.
  8. Coroutines function* () { var response = yield fetch('https://api.github.com/repos/v8/v8/contributors'); var

    json = yield response.json(); var contributors = parseResponse(json); var success = yield fetch('https://hooks.slack.com/...', {…}); if (success.status == 200) { console.log('Success'); } } Coroutines: asynchronous code in blocking style!
  9. Coroutines co(function* () { var response = yield fetch('https://api.github.com/repos/v8/v8/contributors'); var

    json = yield response.json(); var contributors = parseResponse(json); var success = yield fetch('https://hooks.slack.com/...', {…}); if (success.status == 200) { console.log('Success'); } }); github.com/tj/co 920 B (gzip) Make it work using co() function.
  10. Coroutines function co(generator) { var iterator = generator(); return new

    Promise((accept, reject) => { var onResult = lastPromiseResult => { var {value, done} = iterator.next(lastPromiseResult); if (!done) { value.then(onResult, reject); } else { accept(value); } }; onResult(); }); } The simplest implementation of co() function is just 14 lines of code!
  11. Coroutines • facebook/regenerator • babel function* fibonacci() { var n1

    = 1, n2 = 1, sum; yield n1; yield n2; while (true) { sum = n1 + n2; n1 = n2; n2 = sum; yield sum; } } function fibonacci() { var n1, n2, sum; return regeneratorRuntime.wrap(function fibonacci$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: n1 = 1, n2 = 1; _context.next = 3; return n1; case 3: _context.next = 5; return n2; case 5: if (!true) { _context.next = 13; break; } sum = n1 + n2; n1 = n2; n2 = sum; _context.next = 11; return sum; case 11: _context.next = 5; break; case 13: case "end": return _context.stop(); } } }, _marked[0], this); } Works in older browsers using:
  12. ES2016 async / await async function () { var response

    = await fetch('https://api.github.com/repos/v8/v8/contributors'); var json = await response.json(); var contributors = parseResponse(json); var success = await fetch('https://hooks.slack.com/...', {…}); if (success.status == 200) { console.log('Success'); } } • Babel (stage-3) • TC39 Stage 3 Draft tc39.github.io/ecmascript-asyncawait/ • bugs.chromium.org/p/v8/issues/detail?id=4483
  13. ES2016 async / await • yield = await • yield*

    ≟ await* await expects one value. What if we expects a few values or a stream of values?
  14. ES2016 await* var iterator = generator(); for (var value of

    iterator) { // ... } Getting multiple values async function getMessages() { for (var message on new WebSocket(‘…’)) { // ... } } Waiting for multiple values • DOM Events • WebSockets • Server Send Events • Service Workers • XMLHttpRequest • setInterval
  15. ES2016 Generator Iterator ??? interface Iterator { next(): any; }

    interface Generator { next(value): any; throw(error): void; return(value): any; } interface Observer { next(value): void; error(error): void; complete(value): void; } Observer