Slide 1

Slide 1 text

[@bmeurer, @mathias].join(@v8js) [@bmeurer, @mathias].join(@v8js)

Slide 2

Slide 2 text

[@bmeurer, @mathias].join(@v8js)

Slide 3

Slide 3 text

[@bmeurer, @mathias].join(@v8js) function sum(a) { let sum = 0; for (const x of a) { sum += x; } return sum; }

Slide 4

Slide 4 text

[@bmeurer, @mathias].join(@v8js) interface Iterable { [Symbol.iterator]() : Iterator; } interface Iterator { next() : IterResultObject; } interface IterResultObject { value : any; done : boolean; }

Slide 5

Slide 5 text

[@bmeurer, @mathias].join(@v8js) function sum(iterable) { let result = 0; const iterator = iterable[Symbol.iterator](); while (true) { const object = iterator.next(); if (object.done) break; result += object.value; } return result; }

Slide 6

Slide 6 text

[@bmeurer, @mathias].join(@v8js) function sum(iterable) { let result = 0; const iterator = iterable[Symbol.iterator](); while (true) { const object = iterator.next(); if (object.done) break; result += object.value; } return result; }

Slide 7

Slide 7 text

[@bmeurer, @mathias].join(@v8js)

Slide 8

Slide 8 text

[@bmeurer, @mathias].join(@v8js) async function asyncSum(a) { let sum = 0; for await (const x of a) { sum += x; } return sum; }

Slide 9

Slide 9 text

[@bmeurer, @mathias].join(@v8js) interface AsyncIterable { [Symbol.asyncIterator]() : AsyncIterator; } interface AsyncIterator { next() : Promise; } interface IterResultObject { value : any; done : boolean; }

Slide 10

Slide 10 text

[@bmeurer, @mathias].join(@v8js) async function asyncSum(iterable) { let result = 0; const iterator = iterable[Symbol.asyncIterator](); while (true) { const object = await iterator.next(); if (object.done) break; result += object.value; } return result; }

Slide 11

Slide 11 text

[@bmeurer, @mathias].join(@v8js) async function asyncSum(iterable) { let result = 0; const iterator = iterable[Symbol.asyncIterator](); while (true) { const object = await iterator.next(); if (object.done) break; result += object.value; } return result; }

Slide 12

Slide 12 text

[@bmeurer, @mathias].join(@v8js) // All iterables are implicitly async-iterable // via so-called Async-from-Sync Iterator Objects™. asyncSum([1, 2, 3]).then(console.log); // → prints 6

Slide 13

Slide 13 text

[@bmeurer, @mathias].join(@v8js) // All iterables are implicitly async-iterable // via so-called Async-from-Sync Iterator Objects™. asyncSum([1, 2, 3]).then(console.log); // → prints 6 const set = new Set([1, 2, 3, 4, 5, 6]); (async () => { const result = await asyncSum(set); console.log(result); })(); // → prints 21

Slide 14

Slide 14 text

[@bmeurer, @mathias].join(@v8js) // Node streams are async-iterable starting with Node 10. // (WARNING: the feature is considered experimental!) const fs = require('fs'); (async () => { const readStream = fs.createReadStream('file'); for await (const chunk of readStream) { console.log(chunk); } })();

Slide 15

Slide 15 text

[@bmeurer, @mathias].join(@v8js) // For backwards compatibility, use a wrapper like // github:basicdays/node-stream-to-async-iterator (mths.be/bwu) require('core-js/es7/symbol'); const fs = require('fs'); const S2A = require('stream-to-async-iterator'); (async () => { const readStream = fs.createReadStream('file'); for await (const chunk of new S2A(readStream)) { console.log(chunk); } })();

Slide 16

Slide 16 text

[@bmeurer, @mathias].join(@v8js)

Slide 17

Slide 17 text

[@bmeurer, @mathias].join(@v8js) async function* asyncRange(start, end) { for (let i = start; i <= end; ++i) { yield i; } }

Slide 18

Slide 18 text

[@bmeurer, @mathias].join(@v8js) async function* asyncRange(start, end) { for (let i = start; i <= end; ++i) { yield i; } } asyncSum(asyncRange(1, 10)) .then(console.log); // → prints 55

Slide 19

Slide 19 text

[@bmeurer, @mathias].join(@v8js) async function* asyncRandomNumbers() { // This is a web service that returns a random number. const url = 'https://www.random.org/decimal-fractions/' + '?num=1&dec=10&col=1&format=plain&rnd=new'; while (true) { const response = await fetch(url); const text = await response.text(); yield Number(text); } }

Slide 20

Slide 20 text

[@bmeurer, @mathias].join(@v8js) // Generate a bunch of random numbers. (async () => { for await (const number of asyncRandomNumbers()) { console.log(number); if (number > 0.95) break; } })();

Slide 21

Slide 21 text

[@bmeurer, @mathias].join(@v8js)

Slide 22

Slide 22 text

[@bmeurer, @mathias].join(@v8js) const AsyncIteration = { // Turns `type` events produced by a DOM `element` // into an async iterable stream of events. Inspired // by RxJS’s `Observable.fromEvent`. fromEvent: async function* (element, type) { let resolve; const promises = [new Promise(r => resolve = r)]; element.addEventListener(type, event => { resolve(event); promises.push(new Promise(r => resolve = r)); }); while (true) yield await promises.shift(); } };

Slide 23

Slide 23 text

[@bmeurer, @mathias].join(@v8js) Clickety-click! import { AsyncIteration } from './async-iteration.mjs'; (async () => { const button = document.querySelector('button'); const iterator = AsyncIteration.fromEvent(button, 'click'); for await (const event of iterator) { console.log(event); } })();

Slide 24

Slide 24 text

[@bmeurer, @mathias].join(@v8js) Taking it further… Watch your back, Observables !

Slide 25

Slide 25 text

[@bmeurer, @mathias].join(@v8js) const AsyncIteration = { // Generates values that pass the provided condition. // Inspired by RxJS’s `filter`. filter: async function* (iterable, select, thisArg) { for await (const value of iterable) { if (select.call(thisArg, value)) yield value; } } };

Slide 26

Slide 26 text

[@bmeurer, @mathias].join(@v8js) const AsyncIteration = { // Merges N async iterables into a single async iterable. // Inspired by RxJS’s `merge`. merge: async function* (...args) { let resolve; const promises = [new Promise(r => resolve = r)]; async function pump(iterator) { while (true) { const result = await iterator.next(); resolve(result); promises.push(new Promise(r => resolve = r)); } } args.map(iterable => iterable[Symbol.asyncIterator]()).forEach(pump); while (true) { const result = await promises.shift(); if (result.done) break; yield result.value; } } };

Slide 27

Slide 27 text

[@bmeurer, @mathias].join(@v8js) Hello World import { AsyncIteration } from './async-iteration.mjs'; (async () => { const button1 = document.querySelector('#button-1'); const button2 = document.querySelector('#button-2'); const a = AsyncIteration.fromEvent(button1, 'click'); const b = AsyncIteration.fromEvent(button2, 'click'); for await (const event of AsyncIteration.merge(a, b)) { console.log(event.target); } })();

Slide 28

Slide 28 text

[@bmeurer, @mathias].join(@v8js) Live demo "

Slide 29

Slide 29 text

[@bmeurer, @mathias].join(@v8js) RegExp

Slide 30

Slide 30 text

[@bmeurer, @mathias].join(@v8js) RegExp dotAll s

Slide 31

Slide 31 text

[@bmeurer, @mathias].join(@v8js) const input = `
 Lorem ipsum dolor sit amet, consectetur adipiscing hello world elit. Nam sit amet elit id risus aliquam porta. `;


Slide 32

Slide 32 text

[@bmeurer, @mathias].join(@v8js) const input = `
 Lorem ipsum dolor sit amet, consectetur adipiscing hello world elit. Nam sit amet elit id risus aliquam porta. `;

Slide 33

Slide 33 text

[@bmeurer, @mathias].join(@v8js) const input = `
 Lorem ipsum dolor sit amet, consectetur adipiscing hello world elit. Nam sit amet elit id risus aliquam porta. `;
 /hello.world/u.test(input);
 // " ?

Slide 34

Slide 34 text

[@bmeurer, @mathias].join(@v8js) const input = `
 Lorem ipsum dolor sit amet, consectetur adipiscing hello world elit. Nam sit amet elit id risus aliquam porta. `;
 /hello.world/u.test(input);
 // " false !

Slide 35

Slide 35 text

[@bmeurer, @mathias].join(@v8js) const input = `
 Lorem ipsum dolor sit amet, consectetur adipiscing hello world elit. Nam sit amet elit id risus aliquam porta. `;
 /hello.world/u.test(input);
 // " false "

Slide 36

Slide 36 text

[@bmeurer, @mathias].join(@v8js) const input = `
 Lorem ipsum dolor sit amet, consectetur adipiscing hello world elit. Nam sit amet elit id risus aliquam porta. `;
 /hello[\s\S]world/u.test(input);
 // " true #

Slide 37

Slide 37 text

[@bmeurer, @mathias].join(@v8js) const input = `
 Lorem ipsum dolor sit amet, consectetur adipiscing hello world elit. Nam sit amet elit id risus aliquam porta. `;
 /hello[\s\S]world/u.test(input);
 // " true # /hello[^]world/u.test(input);
 // " true $ ♂

Slide 38

Slide 38 text

[@bmeurer, @mathias].join(@v8js) const input = `
 Lorem ipsum dolor sit amet, consectetur adipiscing hello world elit. Nam sit amet elit id risus aliquam porta. `;
 /hello[\s\S]world/u.test(input);
 // " true # /hello[^]world/u.test(input);
 // " true $ ♂

Slide 39

Slide 39 text

[@bmeurer, @mathias].join(@v8js) const input = `
 Lorem ipsum dolor sit amet, consectetur adipiscing hello world elit. Nam sit amet elit id risus aliquam porta. `;
 /hello.world/su.test(input);
 // " true &

Slide 40

Slide 40 text

[@bmeurer, @mathias].join(@v8js) const input = `
 Lorem ipsum dolor sit amet, consectetur adipiscing hello world elit. Nam sit amet elit id risus aliquam porta. `;
 /hello.world/us.test(input);
 // " true '

Slide 41

Slide 41 text

[@bmeurer, @mathias].join(@v8js) RegExp lookbehind assertions *

Slide 42

Slide 42 text

[@bmeurer, @mathias].join(@v8js) // Positive lookahead: const pattern = /\d+(?= dollars)/u; const result = pattern.exec('42 dollars'); // → result[0] === '42'

Slide 43

Slide 43 text

[@bmeurer, @mathias].join(@v8js) // Positive lookahead: const pattern = /\d+(?= dollars)/u; const result = pattern.exec('42 dollars'); // → result[0] === '42' // Negative lookahead: const pattern = /\d+(?! dollars)/u; const result = pattern.exec('42 pesos'); // → result[0] === '42'

Slide 44

Slide 44 text

[@bmeurer, @mathias].join(@v8js) // Positive lookbehind: const pattern = /(?<=\$)\d+/u; const result = pattern.exec('$42'); // → result[0] === '42'

Slide 45

Slide 45 text

[@bmeurer, @mathias].join(@v8js) // Positive lookbehind: const pattern = /(?<=\$)\d+/u; const result = pattern.exec('$42'); // → result[0] === '42' // Negative lookbehind: const pattern = /(?

Slide 46

Slide 46 text

[@bmeurer, @mathias].join(@v8js) // Positive lookbehind: const pattern = /(?<=\$)\d+/u; 'Price: $42'.replace(pattern, '9001'); // → 'Price: $9001'

Slide 47

Slide 47 text

[@bmeurer, @mathias].join(@v8js) // Positive lookbehind: const pattern = /(?<=\$)\d+/u; 'Price: $42'.replace(pattern, '9001'); // → 'Price: $9001' // Negative lookbehind: const pattern = /(?

Slide 48

Slide 48 text

[@bmeurer, @mathias].join(@v8js) RegExp maintainability++

Slide 49

Slide 49 text

[@bmeurer, @mathias].join(@v8js) const pattern = /(\d{4})-(\d{2})-(\d{2})/u; const result = pattern.exec('2018-03-06'); // → result[0] === '2018-03-06'

Slide 50

Slide 50 text

[@bmeurer, @mathias].join(@v8js) const pattern = /(\d{4})-(\d{2})-(\d{2})/u; const result = pattern.exec('2018-03-06'); // → result[0] === '2018-03-06'

Slide 51

Slide 51 text

[@bmeurer, @mathias].join(@v8js) const pattern = /(\d{4})-(\d{2})-(\d{2})/u; const result = pattern.exec('2018-03-06'); // → result[0] === '2018-03-06' // → result[1] === '2018'

Slide 52

Slide 52 text

[@bmeurer, @mathias].join(@v8js) const pattern = /(\d{4})-(\d{2})-(\d{2})/u; const result = pattern.exec('2018-03-06'); // → result[0] === '2018-03-06' // → result[1] === '2018' // → result[2] === '03'

Slide 53

Slide 53 text

[@bmeurer, @mathias].join(@v8js) const pattern = /(\d{4})-(\d{2})-(\d{2})/u; const result = pattern.exec('2018-03-06'); // → result[0] === '2018-03-06' // → result[1] === '2018' // → result[2] === '03' // → result[3] === '06'

Slide 54

Slide 54 text

[@bmeurer, @mathias].join(@v8js) const pattern = /(?\d{4})-(?\d{2})-(?\d{2})/u; const result = pattern.exec('2018-03-06');

Slide 55

Slide 55 text

[@bmeurer, @mathias].join(@v8js) const pattern = /(?\d{4})-(?\d{2})-(?\d{2})/u; const result = pattern.exec('2018-03-06'); // → result.groups.year === '2018'

Slide 56

Slide 56 text

[@bmeurer, @mathias].join(@v8js) const pattern = /(?\d{4})-(?\d{2})-(?\d{2})/u; const result = pattern.exec('2018-03-06'); // → result.groups.year === '2018' // → result.groups.month === '03'

Slide 57

Slide 57 text

[@bmeurer, @mathias].join(@v8js) const pattern = /(?\d{4})-(?\d{2})-(?\d{2})/u; const result = pattern.exec('2018-03-06'); // → result.groups.year === '2018' // → result.groups.month === '03' // → result.groups.day === '06'

Slide 58

Slide 58 text

[@bmeurer, @mathias].join(@v8js) RegExp Iñtërnâtiônàlizætiøn

Slide 59

Slide 59 text

[@bmeurer, @mathias].join(@v8js) const regexGreek = /\p{Script_Extensions=Greek}/u; regexGreek.test('π'); // → true

Slide 60

Slide 60 text

[@bmeurer, @mathias].join(@v8js) const regexGreek = /\p{Script_Extensions=Greek}/u; regexGreek.test('π'); // → true const regexAscii = /\p{ASCII}/u; regexAscii.test('_'); // → true

Slide 61

Slide 61 text

[@bmeurer, @mathias].join(@v8js) const regexGreek = /\p{Script_Extensions=Greek}/u; regexGreek.test('π'); // → true const regexAscii = /\p{ASCII}/u; regexAscii.test('_'); // → true const regexMath = /\p{Math}/u; regexMath.test('≠'); // → true

Slide 62

Slide 62 text

[@bmeurer, @mathias].join(@v8js) const pattern = /[\p{Letter}\p{White_Space}]+/u; pattern.test('Γειά σου κόσμε'); // → true

Slide 63

Slide 63 text

[@bmeurer, @mathias].join(@v8js) :valid { background: lightgreen; } :invalid { background: pink; }

Slide 64

Slide 64 text

[@bmeurer, @mathias].join(@v8js)

Slide 65

Slide 65 text

[@bmeurer, @mathias].join(@v8js) // Rest elements for array destructuring assignment: const primes = [2, 3, 5, 7, 11]; const [first, second, ...rest] = primes; console.log(first); // 2 console.log(second); // 3 console.log(rest); // [5, 7, 11]

Slide 66

Slide 66 text

[@bmeurer, @mathias].join(@v8js) // Rest elements for array destructuring assignment: const primes = [2, 3, 5, 7, 11]; const [first, second, ...rest] = primes; console.log(first); // 2 console.log(second); // 3 console.log(rest); // [5, 7, 11] // Spread elements for array literals: const primesCopy = [first, second, ...rest]; console.log(primesCopy); // [2, 3, 5, 7, 11]

Slide 67

Slide 67 text

[@bmeurer, @mathias].join(@v8js) // Rest properties for object destructuring assignment: const person = { firstName: 'Sebastian', lastName: 'Markbåge', country: 'USA', state: 'CA', }; const { firstName, lastName, ...rest } = person; console.log(firstName); // Sebastian console.log(lastName); // Markbåge console.log(rest); // { country: 'USA', state: 'CA' }

Slide 68

Slide 68 text

[@bmeurer, @mathias].join(@v8js) // Spread properties for object literals: const personCopy = { firstName, lastName, ...rest }; console.log(personCopy); // { firstName: 'Sebastian', // lastName: 'Markbåge', // country: 'USA', // state: 'CA' }

Slide 69

Slide 69 text

[@bmeurer, @mathias].join(@v8js) // Shallow-clone an object: const data = { x: 42, y: 27, label: 'Treasure' }; // The old way: const clone1 = Object.assign({}, data);

Slide 70

Slide 70 text

[@bmeurer, @mathias].join(@v8js) // Shallow-clone an object: const data = { x: 42, y: 27, label: 'Treasure' }; // The old way: const clone1 = Object.assign({}, data); // The new way: const clone2 = { ...data }; // Either results in: // { x: 42, y: 27, label: 'Treasure' }

Slide 71

Slide 71 text

[@bmeurer, @mathias].join(@v8js) // Merge two objects: const defaultSettings = { logWarnings: false, logErrors: false }; const userSettings = { logErrors: true }; // The old way: const settings1 = Object.assign({}, defaultSettings, userSettings);

Slide 72

Slide 72 text

[@bmeurer, @mathias].join(@v8js) // Merge two objects: const defaultSettings = { logWarnings: false, logErrors: false }; const userSettings = { logErrors: true }; // The old way: const settings1 = Object.assign({}, defaultSettings, userSettings); // The new way: const settings2 = { ...defaultSettings, ...userSettings }; // Either results in: // { logWarnings: false, logErrors: true }

Slide 73

Slide 73 text

[@bmeurer, @mathias].join(@v8js) Promise.prototype.finally

Slide 74

Slide 74 text

[@bmeurer, @mathias].join(@v8js) fetchAndDisplay({ url: someUrl, element: document.querySelector('#output') });

Slide 75

Slide 75 text

[@bmeurer, @mathias].join(@v8js) const fetchAndDisplay = ({ url, element }) => { showLoadingSpinner(); fetch(url) .then((response) => response.text()) .then((text) => { element.textContent = text; hideLoadingSpinner(); }) .catch((error) => { element.textContent = error.message; hideLoadingSpinner(); }); };

Slide 76

Slide 76 text

[@bmeurer, @mathias].join(@v8js) const fetchAndDisplay = ({ url, element }) => { showLoadingSpinner(); fetch(url) .then((response) => response.text()) .then((text) => { element.textContent = text; hideLoadingSpinner(); }) .catch((error) => { element.textContent = error.message; hideLoadingSpinner(); }); };

Slide 77

Slide 77 text

[@bmeurer, @mathias].join(@v8js) const fetchAndDisplay = ({ url, element }) => { showLoadingSpinner(); fetch(url) .then((response) => response.text()) .then((text) => { element.textContent = text; hideLoadingSpinner(); }) .catch((error) => { element.textContent = error.message; hideLoadingSpinner(); }); };

Slide 78

Slide 78 text

[@bmeurer, @mathias].join(@v8js) const fetchAndDisplay = ({ url, element }) => { showLoadingSpinner(); fetch(url) .then((response) => response.text()) .then((text) => { element.textContent = text; hideLoadingSpinner(); }) .catch((error) => { element.textContent = error.message; hideLoadingSpinner(); }); };

Slide 79

Slide 79 text

[@bmeurer, @mathias].join(@v8js) const fetchAndDisplay = ({ url, element }) => { showLoadingSpinner(); fetch(url) .then((response) => response.text()) .then((text) => { element.textContent = text; hideLoadingSpinner(); }) .catch((error) => { element.textContent = error.message; hideLoadingSpinner(); }); };

Slide 80

Slide 80 text

[@bmeurer, @mathias].join(@v8js) const fetchAndDisplay = ({ url, element }) => { showLoadingSpinner(); fetch(url) .then((response) => response.text()) .then((text) => { element.textContent = text; hideLoadingSpinner(); }) .catch((error) => { element.textContent = error.message; hideLoadingSpinner(); }); };

Slide 81

Slide 81 text

[@bmeurer, @mathias].join(@v8js) const fetchAndDisplay = ({ url, element }) => { showLoadingSpinner(); fetch(url) .then((response) => response.text()) .then((text) => { element.textContent = text; }) .catch((error) => { element.textContent = error.message; }) .finally(() => { hideLoadingSpinner(); }); };

Slide 82

Slide 82 text

[@bmeurer, @mathias].join(@v8js) const fetchAndDisplay = async ({ url, element }) => { showLoadingSpinner(); try { const response = await fetch(url); const text = await response.text(); element.textContent = text; } catch (error) { element.textContent = error.message; } finally { hideLoadingSpinner(); } };

Slide 83

Slide 83 text

[@bmeurer, @mathias].join(@v8js) More info ● ● Promise.prototype.finally ● ●

Slide 84

Slide 84 text

[@bmeurer, @mathias].join(@v8js) Register now: mths.be/bwp

Slide 85

Slide 85 text

[@bmeurer, @mathias].join(@v8js)