TC39 Process
• Five stage process for converting
an idea to a standard feature.
• https://tc39.github.io/process-
document
Slide 12
Slide 12 text
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; }
Slide 13
Slide 13 text
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; }
}
Slide 14
Slide 14 text
Stage 2 - Draft
• Precise syntax and semantics;
initial spec.
• Object rest and spread properties
let obj = { ...otherObj, x: 'foo' };
Stage 4 - Finished
• Ready to be included in the next
ES standard.
• No proposals at Stage 4 at the
moment.
Slide 18
Slide 18 text
No content
Slide 19
Slide 19 text
Slide 20
Slide 20 text
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
Slide 21
Slide 21 text
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
Slide 22
Slide 22 text
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.
Slide 23
Slide 23 text
Solution?
Slide 24
Slide 24 text
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
Slide 25
Slide 25 text
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
Slide 26
Slide 26 text
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
Slide 27
Slide 27 text
More complex and
less declarative.
Slide 28
Slide 28 text
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
Slide 29
Slide 29 text
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
Slide 30
Slide 30 text
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
function enumerable(target, name, descriptor) {
descriptor.enumerable = true;
return descriptor;
}
Object being modified
(e.g. prototype or a
class based on usage)
Slide 34
Slide 34 text
function enumerable(target, name, descriptor) {
descriptor.enumerable = true;
return descriptor;
}
Name of property
that will be added
to target
Slide 35
Slide 35 text
function enumerable(target, name, descriptor) {
descriptor.enumerable = true;
return descriptor;
}
Object literal
defining the
property that will be
added to target
Slide 36
Slide 36 text
function enumerable(target, name, descriptor) {
descriptor.enumerable = true;
return descriptor;
} Optional if you mutate
descriptor. Useful if
you want to create
new descriptor.
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.
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);
}
}
Slide 43
Slide 43 text
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);
}
}
Slide 44
Slide 44 text
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
Slide 45
Slide 45 text
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
Slide 46
Slide 46 text
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
Slide 47
Slide 47 text
Slide 48
Slide 48 text
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
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.
Slide 60
Slide 60 text
Slide 61
Slide 61 text
And the moment
you’ve been awaiting.
Slide 62
Slide 62 text
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
async function printOrder(orderId) {
const order = await fetchOrder(orderId);
console.log(order);
}
printOrder(1);
Slide 69
Slide 69 text
async function printOrder(orderId) {
const order = await fetchOrder(orderId);
console.log(order);
}
printOrder(1);
Declare a
function as
async
Slide 70
Slide 70 text
async function printOrder(orderId) {
const order = await fetchOrder(orderId);
console.log(order);
}
printOrder(1);
Await a promise and
obtain the fulfilled/
resolved value.
Nonblocking.
Slide 71
Slide 71 text
async function printOrder(orderId) {
const order = await fetchOrder(orderId);
console.log(order);
}
printOrder(1);
x x
Slide 72
Slide 72 text
How do async
functions work at a
high level?
Slide 73
Slide 73 text
async function printOrder(orderId) {
const order = await fetchOrder(orderId);
console.log(order);
}
printOrder(1);
Slide 74
Slide 74 text
Invoke function.
async function printOrder(orderId) {
const order = await fetchOrder(orderId);
console.log(order);
}
printOrder(1);
Slide 75
Slide 75 text
async function printOrder(orderId) {
const order = await fetchOrder(orderId);
console.log(order);
}
printOrder(1);
Encounter await.
Slide 76
Slide 76 text
async function printOrder(orderId) {
const order = await fetchOrder(orderId);
console.log(order);
}
printOrder(1);
Wrap awaited expression in
Promise.
Slide 77
Slide 77 text
Wrap awaited expression in
Promise.
async function printOrder(orderId) {
const promise = Promise.resolve(
fetchOrder(orderId)
);
console.log(order);
}
printOrder(1);
Slide 78
Slide 78 text
Wrap remaining code in a then callback.
async function printOrder(orderId) {
const promise = Promise.resolve(
fetchOrder(orderId)
);
console.log(order);
}
printOrder(1);
Slide 79
Slide 79 text
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.
Slide 80
Slide 80 text
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);
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]);
Slide 88
Slide 88 text
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.
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.”
Slide 91
Slide 91 text
Sequential vs. Parallel
Demo
Slide 92
Slide 92 text
Moral: don’t use
sequential awaits
unless you need
serialization!
Slide 93
Slide 93 text
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.
Slide 94
Slide 94 text
Slide 95
Slide 95 text
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!