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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  5. Many proposed
    features, several
    coming after ES7.

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  8. 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 full-size slide

  9. 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 full-size slide

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

    View full-size slide

  11. 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 full-size slide

  12. Stage 3 - Candidate
    • Async Functions

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

    View full-size slide

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

    View full-size slide

  14. 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 full-size slide

  15. 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 full-size slide

  16. 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 full-size slide

  17. 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 full-size slide

  18. 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 full-size slide

  19. 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 full-size slide

  20. More complex and
    less declarative.

    View full-size slide

  21. 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 full-size slide

  22. 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 full-size slide

  23. 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 full-size slide

  24. How do decorators
    work?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  30. 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 full-size slide

  31. Decorators can take
    arguments too.

    View full-size slide

  32. 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 full-size slide

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

    View full-size slide

  34. What about decorated
    classes?

    View full-size slide

  35. 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 full-size slide

  36. 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 full-size slide

  37. 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 full-size slide

  38. 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 full-size slide

  39. 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 full-size slide

  40. 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 full-size slide

  41. 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 full-size slide

  42. 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 full-size slide

  43. 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 full-size slide

  44. 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 full-size slide

  45. 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 full-size slide

  46. 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 full-size slide

  47. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  50. 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 full-size slide

  51. 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 full-size slide

  52. And the moment
    you’ve been awaiting.

    View full-size slide

  53. 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 full-size slide

  54. 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 full-size slide

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

    View full-size slide

  56. Workarounds.

    View full-size slide

  57. 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 full-size slide

  58. Asynchronous code
    with synchronous-like
    syntax.

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  61. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  70. 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 full-size slide

  71. 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 full-size slide

  72. Error Handling.

    View full-size slide

  73. 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 full-size slide

  74. 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 full-size slide

  75. 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 full-size slide

  76. 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 full-size slide

  77. Sequential vs. Parallel

    View full-size slide

  78. 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 full-size slide

  79. 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 full-size slide

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

    View full-size slide

  81. 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 full-size slide

  82. Sequential vs. Parallel
    Demo

    View full-size slide

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

    View full-size slide

  84. 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 full-size slide

  85. 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 full-size slide

  86. 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 full-size slide

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

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

    View full-size slide