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

HTML5DevConf ES7 and Beyond!

HTML5DevConf ES7 and Beyond!

Jeremy Fairbank

October 20, 2015
Tweet

More Decks by Jeremy Fairbank

Other Decks in Programming

Transcript

  1. ES7
    and Beyond!
    Jeremy Fairbank
    jeremyfairbank.com
    @elpapapollo

    View Slide

  2. Hi, I’m Jeremy
    jfairbank @elpapapollo blog.jeremyfairbank.com

    View Slide

  3. We help brands excel.
    simplybuilt.com
    Your website, SimplyBuilt.
    pushagency.io

    View Slide

  4. ES7

    View Slide

  5. ES2016
    ES.next
    ES.later
    ES.whatisgoingon?
    ES7

    View Slide

  6. View Slide

  7. Many proposed
    features, several
    coming after ES7.

    View Slide

  8. View Slide

  9. View Slide

  10. Transpiler
    • Convert ES6/ES7/ES.later code
    into equivalent ES5 code.
    • Babel • babeljs.io

    View Slide

  11. TC39 Process
    • Five stage process for converting
    an idea to a standard feature.
    • https://tc39.github.io/process-
    document

    View Slide

  12. Stage 0 - Strawman
    • Informal idea or proposal for
    change or addition.
    • Function bind operator 

    • Do expressions
    foo::bar();
    let y = do { let x = 40; x + 2; }

    View Slide

  13. Stage 1 - Proposal
    • Make a case for addition by
    describing use cases, challenges,
    high-level API.
    • Class and property decorators
    class Foo {
    @memoize
    bar() { return 42; }
    }

    View Slide

  14. Stage 2 - Draft
    • Precise syntax and semantics;
    initial spec.
    • Object rest and spread properties
    let obj = { ...otherObj, x: 'foo' };

    View Slide

  15. Stage 3 - Candidate
    • Completed spec; needs reviewing for
    refinements.
    • SIMD - Single instruction, multiple data
    • Exponentiation operator

    • Array.prototype.includes

    3 ** 2; // 9
    [1, 2, 3].includes(2); // true

    View Slide

  16. Stage 3 - Candidate
    • Async Functions

    async function loadOrders() {
    const orders = await fetchJson('/orders');
    console.log(orders);
    }

    View Slide

  17. Stage 4 - Finished
    • Ready to be included in the next
    ES standard.
    • No proposals at Stage 4 at the
    moment.

    View Slide

  18. View Slide


  19. View Slide

  20. Class Decorators
    • Annotate and modify classes and their
    properties.
    • Use @ and an expression that evaluates
    to a function.
    • Decorator precedes a class or method
    definition.
    • github.com/wycats/javascript-decorators

    View Slide

  21. class Person {
    constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    }
    get fullName() {
    return `${this.firstName} ${this.lastName}`;
    }
    }
    const p = new Person('Jeremy', 'Fairbank');
    for (const key in p) {
    console.log(`${key}: ${p[key]}`);
    }
    // firstName: Jeremy
    // lastName: Fairbank

    View Slide

  22. class Person {
    constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    }
    get fullName() {
    return `${this.firstName} ${this.lastName}`;
    }
    }
    const p = new Person('Jeremy', 'Fairbank');
    for (const key in p) {
    console.log(`${key}: ${p[key]}`);
    }
    // firstName: Jeremy
    // lastName: Fairbank
    Getters are
    nonenumerable
    by default.

    View Slide

  23. Solution?

    View Slide

  24. class Person {
    constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    }
    }
    Object.defineProperty(Person.prototype, 'fullName', {
    enumerable: true,
    get() {
    return `${this.firstName} ${this.lastName}`;
    }
    });
    const p = new Person('Jeremy', 'Fairbank');
    for (const key in p) {
    console.log(`${key}: ${p[key]}`);
    }
    // firstName: Jeremy
    // lastName: Fairbank
    // fullName: Jeremy Fairbank

    View Slide

  25. class Person {
    constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    }
    }
    Object.defineProperty(Person.prototype, 'fullName', {
    enumerable: true,
    get() {
    return `${this.firstName} ${this.lastName}`;
    }
    });
    const p = new Person('Jeremy', 'Fairbank');
    for (const key in p) {
    console.log(`${key}: ${p[key]}`);
    }
    // firstName: Jeremy
    // lastName: Fairbank
    // fullName: Jeremy Fairbank

    View Slide

  26. class Person {
    constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    }
    }
    Object.defineProperty(Person.prototype, 'fullName', {
    enumerable: true,
    get() {
    return `${this.firstName} ${this.lastName}`;
    }
    });
    const p = new Person('Jeremy', 'Fairbank');
    for (const key in p) {
    console.log(`${key}: ${p[key]}`);
    }
    // firstName: Jeremy
    // lastName: Fairbank
    // fullName: Jeremy Fairbank

    View Slide

  27. More complex and
    less declarative.

    View Slide

  28. class Person {
    constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    }
    @enumerable
    get fullName() {
    return `${this.firstName} ${this.lastName}`;
    }
    }
    const p = new Person('Jeremy', 'Fairbank');
    for (const key in p) {
    console.log(`${key}: ${p[key]}`);
    }
    // firstName: Jeremy
    // lastName: Fairbank
    // fullName: Jeremy Fairbank

    View Slide

  29. class Person {
    constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    }
    @enumerable
    get fullName() {
    return `${this.firstName} ${this.lastName}`;
    }
    }
    const p = new Person('Jeremy', 'Fairbank');
    for (const key in p) {
    console.log(`${key}: ${p[key]}`);
    }
    // firstName: Jeremy
    // lastName: Fairbank
    // fullName: Jeremy Fairbank

    View Slide

  30. class Person {
    constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    }
    @enumerable
    get fullName() {
    return `${this.firstName} ${this.lastName}`;
    }
    }
    const p = new Person('Jeremy', 'Fairbank');
    for (const key in p) {
    console.log(`${key}: ${p[key]}`);
    }
    // firstName: Jeremy
    // lastName: Fairbank
    // fullName: Jeremy Fairbank

    View Slide

  31. How do decorators
    work?

    View Slide

  32. function enumerable(target, name, descriptor) {
    descriptor.enumerable = true;
    return descriptor;
    }

    View Slide

  33. function enumerable(target, name, descriptor) {
    descriptor.enumerable = true;
    return descriptor;
    }
    Object being modified
    (e.g. prototype or a
    class based on usage)

    View Slide

  34. function enumerable(target, name, descriptor) {
    descriptor.enumerable = true;
    return descriptor;
    }
    Name of property
    that will be added
    to target

    View Slide

  35. function enumerable(target, name, descriptor) {
    descriptor.enumerable = true;
    return descriptor;
    }
    Object literal
    defining the
    property that will be
    added to target

    View Slide

  36. function enumerable(target, name, descriptor) {
    descriptor.enumerable = true;
    return descriptor;
    } Optional if you mutate
    descriptor. Useful if
    you want to create
    new descriptor.

    View Slide

  37. Transpiling
    @enumerable
    get fullName() {
    return `${this.firstName} ${this.lastName}`;
    }
    let descriptor = {
    get() { return `${this.firstName} ${this.lastName}`; },
    enumerable: false,
    configurable: true
    };
    descriptor = enumerable(Person.prototype, 'fullName', descriptor) ||
    descriptor;
    Object.defineProperty(Person.prototype, 'fullName', descriptor);

    View Slide

  38. Decorators can take
    arguments too.

    View Slide

  39. class Person {
    constructor(name) {
    this.name = name;
    }
    @alias('sayHello')
    greet() {
    console.log(`Hello. My name is ${this.name}.`);
    }
    }
    const person = new Person('Jeremy');
    person.greet(); // Hello. My name is Jeremy.
    person.sayHello(); // Hello. My name is Jeremy.

    View Slide

  40. function alias(aliasName) {
    return (target, name, descriptor) => {
    const newDescriptor = Object.assign({}, descriptor, {
    value(...args) {
    return this[name](...args);
    }
    });
    Object.defineProperty(
    target,
    aliasName,
    newDescriptor
    );
    };
    }

    View Slide

  41. What about decorated
    classes?

    View Slide

  42. const Quadrupedal = {
    walk() {
    console.log(`${this.name} walks with four legs`);
    }
    };
    @mixin(Quadrupedal, EventEmitter.prototype)
    class Dog {
    constructor(name, age) {
    this.name = name;
    this.age = age;
    }
    haveBirthday() {
    this.age++;
    this.emit('birthday', this);
    }
    }

    View Slide

  43. const Quadrupedal = {
    walk() {
    console.log(`${this.name} walks with four legs`);
    }
    };
    @mixin(Quadrupedal, EventEmitter.prototype)
    class Dog {
    constructor(name, age) {
    this.name = name;
    this.age = age;
    }
    haveBirthday() {
    this.age++;
    this.emit('birthday', this);
    }
    }

    View Slide

  44. const dog = new Dog('Tucker', 8);
    dog.on('birthday', d => {
    console.log(`${d.name} just turned ${d.age}`);
    });
    dog.haveBirthday(); // Tucker just turned 9
    dog.walk(); // Tucker walks with four legs

    View Slide

  45. const dog = new Dog('Tucker', 8);
    dog.on('birthday', d => {
    console.log(`${d.name} just turned ${d.age}`);
    });
    dog.haveBirthday(); // Tucker just turned 9
    dog.walk(); // Tucker walks with four legs
    From
    EventEmitter
    From Quadrupedal

    View Slide

  46. Why Class Decorators?
    • Extremely powerful and expressive syntax for
    class/property modification.
    • Mixins — avoid fragile base class or multiple
    inheritance issues.
    • @autobind, @deprecate, @memoize, @readonly,
    and more!
    • github.com/jayphelps/core-decorators.js

    View Slide



  47. View Slide

  48. Object Rest and Spread
    • Gives objects rest and spread capabilities like
    arrays in ES6.
    • Use ... operator.
    • Rest operation is like pick function in lodash
    or a clone function.
    • Spread operation is like syntactical sugar for
    Object.assign.
    • github.com/sebmarkbage/ecmascript-rest-spread

    View Slide

  49. Rest - Destructuring
    import isEqual from 'lodash/lang/isEqual';
    const assert = ::console.assert;
    const obj = { a: 42, b: 'foo', c: 5, d: 'bar' };
    const { a, b, ...rest } = obj;
    assert(a === 42);
    assert(b === 'foo');
    assert(isEqual(rest, { c: 5, d: 'bar' }));

    View Slide

  50. Rest - Destructuring
    import isEqual from 'lodash/lang/isEqual';
    const assert = ::console.assert;
    const obj = { a: 42, b: 'foo', c: 5, d: 'bar' };
    const { a, b, ...rest } = obj;
    assert(a === 42);
    assert(b === 'foo');
    assert(isEqual(rest, { c: 5, d: 'bar' }));

    View Slide

  51. Spread
    const obj = { x: 42, y: 100 };
    const newObj = { z: 5, ...obj };
    const otherObj = { ...obj, x: 13 };
    assert(isEqual(newObj, { x: 42, y: 100, z: 5 }));
    assert(isEqual(otherObj, { x: 13, y: 100 }));

    View Slide

  52. const obj = { x: 42, y: 100 };
    const newObj = { z: 5, ...obj };
    const otherObj = { ...obj, x: 13 };
    assert(isEqual(newObj, { x: 42, y: 100, z: 5 }));
    assert(isEqual(otherObj, { x: 13, y: 100 }));
    Spread

    View Slide

  53. const obj = { x: 42, y: 100 };
    const newObj = { z: 5, ...obj };
    const otherObj = { ...obj, x: 13 };
    assert(isEqual(newObj, { x: 42, y: 100, z: 5 }));
    assert(isEqual(otherObj, { x: 13, y: 100 }));
    Spread

    View Slide

  54. const obj = { x: 42, y: 100 };
    const newObj = { z: 5, ...obj };
    const otherObj = { ...obj, x: 13 };
    assert(isEqual(newObj, { x: 42, y: 100, z: 5 }));
    assert(isEqual(otherObj, { x: 13, y: 100 }));
    Spread

    View Slide

  55. Transpiling Spread
    const newObj = {
    z: 5,
    ...obj
    };
    const newObj = Object.assign({
    z: 5
    }, obj);
    const otherObj = {
    ...obj,
    x: 13
    };
    const otherObj = Object.assign({},
    obj, { x: 13 }
    );

    View Slide

  56. Usage: Default Options
    function createDog(options) {
    const defaults = {
    breed: 'Sheltie',
    name: 'Tucker'
    };
    return { ...defaults, ...options };
    }

    View Slide

  57. Usage: Default Options
    createDog();
    // { breed: 'Sheltie', name: 'Tucker' }
    createDog({
    breed: 'Golden Retriever',
    name: 'Shadow'
    });
    createDog({ name: 'Chance' });
    // { breed: 'Sheltie', name: 'Chance' }

    View Slide

  58. Usage: Redux
    const INITIAL_CONTACT = { name: '', email: '' };
    function reducer(state = INITIAL_CONTACT, action) {
    switch(action.type) {
    case 'UPDATE_NAME':
    return { ...state, name: action.payload };
    case 'UPDATE_EMAIL':
    return { ...state, email: action.payload };
    default:
    return state;
    }
    }

    View Slide

  59. Why Object Rest/Spread?
    • Succinct syntax instead of Object.assign
    calls.
    • Pick/omit object properties.
    • Clone objects. (WARNING: Not a deep clone.)
    • Provide overridable default options in a
    function.

    View Slide



  60. View Slide

  61. And the moment
    you’ve been awaiting.

    View Slide

  62. Async Functions
    • Provide synchronous-like syntax for
    asynchronous code.
    • Prepend function with async.
    • Use await operator on a promise to
    obtain a fulfilled promise value.
    • github.com/tc39/ecmascript-asyncawait

    View Slide

  63. function fetchCustomerNameForOrder(orderId, done, fail) {
    fetchOrder(orderId, function(err, order) {
    if (err) {
    logError(err);
    fail(err);
    } else {
    fetchCustomer(
    order.customerId,
    function(err, customer) {
    if (err) {
    logError(err);
    fail(err);
    } else {
    done(customer.name);
    }
    }
    )
    }
    })
    }

    View Slide

  64. function fetchCustomerNameForOrder(orderId) {
    return fetchOrder(orderId)
    .then(order => fetchCustomer(order.customerId))
    .then(customer => customer.name)
    .catch(err => {
    logError(err);
    throw err;
    });
    }

    View Slide

  65. Workarounds.

    View Slide

  66. async function fetchCustomerNameForOrder(orderId) {
    let order, customer;
    try {
    order = await fetchOrder(orderId);
    } catch(err) {
    logError(err);
    throw err;
    }
    try {
    customer = await fetchCustomer(order.customerId);
    return customer.name;
    } catch(err) {
    logError(err);
    throw err;
    }
    }

    View Slide

  67. Asynchronous code
    with synchronous-like
    syntax.

    View Slide

  68. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);

    View Slide

  69. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);
    Declare a
    function as
    async

    View Slide

  70. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);
    Await a promise and
    obtain the fulfilled/
    resolved value.
    Nonblocking.

    View Slide

  71. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);
    x x

    View Slide

  72. How do async
    functions work at a
    high level?

    View Slide

  73. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);

    View Slide

  74. Invoke function.
    async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);

    View Slide

  75. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);
    Encounter await.

    View Slide

  76. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);
    Wrap awaited expression in
    Promise.

    View Slide

  77. Wrap awaited expression in
    Promise.
    async function printOrder(orderId) {
    const promise = Promise.resolve(
    fetchOrder(orderId)
    );
    console.log(order);
    }
    printOrder(1);

    View Slide

  78. Wrap remaining code in a then callback.
    async function printOrder(orderId) {
    const promise = Promise.resolve(
    fetchOrder(orderId)
    );
    console.log(order);
    }
    printOrder(1);

    View Slide

  79. function printOrder(orderId) {
    const promise = Promise.resolve(
    fetchOrder(orderId)
    );
    return promise.then(order => {
    console.log(order);
    return Promise.resolve(undefined);
    });
    }
    printOrder(1);
    Wrap remaining code in a then callback.

    View Slide

  80. No explicit return, so return a
    resolved undefined value.
    function printOrder(orderId) {
    const promise = Promise.resolve(
    fetchOrder(orderId)
    );
    return promise.then(order => {
    console.log(order);
    return Promise.resolve(undefined);
    });
    }
    printOrder(1);

    View Slide

  81. Error Handling.

    View Slide

  82. function printOrder(orderId) {
    fetchOrder(orderId)
    .then(order => console.log(order))
    .catch(e => console.log('error retrieving order', e));
    }
    async function printOrder(orderId) {
    try {
    const order = await fetchOrder(orderId);
    console.log(order);
    } catch(e) {
    console.log('error retrieving order', e);
    }
    }

    View Slide

  83. function printOrder(orderId) {
    fetchOrder(orderId)
    .then(order => console.log(order))
    .catch(e => console.log('error retrieving order', e));
    }
    async function printOrder(orderId) {
    try {
    const order = await fetchOrder(orderId);
    console.log(order);
    } catch(e) {
    console.log('error retrieving order', e);
    }
    }

    View Slide

  84. function printOrder(orderId) {
    fetchOrder(orderId)
    .then(order => console.log(order))
    .catch(e => console.log('error retrieving order', e));
    }
    async function printOrder(orderId) {
    try {
    const order = await fetchOrder(orderId);
    console.log(order);
    } catch(e) {
    console.log('error retrieving order', e);
    }
    }

    View Slide

  85. function printOrder(orderId) {
    fetchOrder(orderId)
    .then(order => console.log(order))
    .catch(e => console.log('error retrieving order', e));
    }
    async function printOrder(orderId) {
    try {
    const order = await fetchOrder(orderId);
    console.log(order);
    } catch(e) {
    console.log('error retrieving order', e);
    }
    }

    View Slide

  86. Sequential vs. Parallel

    View Slide

  87. Sequential
    async function printOrders(orderIds) {
    const orders = [];
    for (const id of orderIds) {
    const order = await fetchOrder(id);
    orders.push(order);
    }
    console.log(orders);
    }
    printOrders([1, 2, 3]);

    View Slide

  88. Sequential
    async function printOrders(orderIds) {
    const orders = [];
    for (const id of orderIds) {
    const order = await fetchOrder(id);
    orders.push(order);
    }
    console.log(orders);
    }
    printOrders([1, 2, 3]);
    Each step of loop has to
    wait. Issue if requests
    aren’t dependent on each
    other.

    View Slide

  89. Parallel
    async function printOrders(orderIds) {
    const orders = await Promise.all(
    orderIds.map(id => fetchOrder(id))
    );
    console.log(orders);
    }
    printOrders([1, 2, 3]);

    View Slide

  90. Parallel
    async function printOrders(orderIds) {
    const orders = await Promise.all(
    orderIds.map(id => fetchOrder(id))
    );
    console.log(orders);
    }
    printOrders([1, 2, 3]);
    Make all requests at
    once and resolve with
    Promise.all. Allows for
    “parallelism.”

    View Slide

  91. Sequential vs. Parallel
    Demo

    View Slide

  92. Moral: don’t use
    sequential awaits
    unless you need
    serialization!

    View Slide

  93. Why Async Functions?
    • Synchronous-looking asynchronous
    code.
    • Gain back use of native language flow
    control constructs with asynchronous
    code (e.g. for..of, try/catch).
    • Asynchronous coroutines.

    View Slide


  94. View Slide

  95. Other upcoming features
    • Stage 3: SIMD, Exponentiation operator,
    Array.prototype.includes
    • Stage 2: Object.observe
    • Stage 1: Typed objects, class property
    initializers, shared memory and atomics,
    Observable
    • Stage 0: function bind operator, do expressions
    • Many more!

    View Slide

  96. Resources
    • https://github.com/tc39/ecma262
    • http://babeljs.io/
    • $ babel --stage 0 myAwesomeES7code.js
    • https://tc39.github.io/process-document/
    • https://esdiscuss.org/
    • http://www.ecma-international.org/memento/
    contribute_TC39_Royalty_Free_Task_Group.php

    View Slide

  97. Thanks!
    Code:
    github.com/jfairbank/es7-and-beyond-talk
    Slides:

    speakerdeck.com/jfairbank/html5devconf-es7-and-beyond
    Jeremy Fairbank
    jeremyfairbank.com
    @elpapapollo

    View Slide