Getting Started with ES6 and ES7 JavaScript

ES6 and ES7 Intro Image

Class 4

Today's Topics

  1. Review at-home challenges from previous class
  2. Promises
  3. Fetch
  4. The Fetch Helper
  5. Generator Functions
  6. Iterables
  7. Sets and Maps
  8. Classes
  9. Encapsulating Code with Modules
  10. Transpiling Setup
  11. Additional Resources

Review: Refactor a detectCollision function

Although detectCollision works as intended, we can refactor it to improve its performance and readability. Reformat detectCollision to use destructuring and an array helper.

Hint: We'll return a single matched object. Which array helper would be best in this case?

View Solution »

Review: Mixed Destructuring

const node = {
  location: {
    start: {
      line: 1, // first
      column: 1 // second
    },
    end: {
      line: 1,
      column: 4
    }
  },
  range: [0, 3] // third: outputs first array element
};

const {
  location: { start },
  range: [ index ]
} = node;

console.log(start.line); // 1
console.log(start.column); // 1
console.log(index); // 0

What would the following output to the console and why?

Answer:

1
1
0
View CodePen »

Promises

Problems with Asynchronous Requests

Promises Start
Promises Step 1
Promises Step 2
Promises Step 3
Promises Final

The Ideal Scenario

Promises Step 1
Promises Step 2
Promises Step 3
Promises Final Step

Three Promise States

Promises States Initial Step
Promises Step 1
Promises Step 2
Promises Step 3

Creating a Promise

View CodePen »
  • You'll need:
    1. The Promise object, which accepts a callback function (called the executor) with resolve and reject arguments
    2. resolve() and reject() are invoked depending on certain conditions
    3. then() and catch()blocks attached to the Promise object
const promise = new Promise((resolve, reject) => {

});







                
const promise = new Promise((resolve, reject) => {

});

promise.then(() => {
  console.log('my resolution message');
});



                
const promise = new Promise((resolve, reject) => {

});

promise.then(() => {
  console.log('my resolution message');
});

promise.catch(() => {
  console.log('my rejection message');
});
const promise = new Promise((resolve, reject) => {
  resolve(); // outputs 'my resolution message'
});

promise.then(() => {
  console.log('my resolution message');
});

promise.catch(() => {
  console.log('my rejection message');
});
const promise = new Promise((resolve, reject) => {
  reject(); // outputs 'my rejection message'
});

promise.then(() => {
  console.log('my resolution message');
});

promise.catch(() => {
  console.log('my rejection message');
});
const promise = new Promise((resolve, reject) => {
  resolve(5); // chain callbacks and pass info between them
})
  .then((value) => {
    console.log(value); // 5
  })
  .catch(() => {
    console.log('my rejection message');
  });

                

 

Always include a rejection handler! Otherwise your promise may silently fail.

Activity

Using Promises

Activity 1

See the Pen Promises Activity by Liz Shaw (@anythingcodes) on CodePen.

View Solution

Activity 2

See the Pen Promises with an API by Liz Shaw (@anythingcodes) on CodePen.

View Solution

Grouping Multiple Promises

Combine promises in an iterable, such as an array

  1. Promise.all() waits for all promises in the group to fulfill
  2. Promise.race() is fulfilled when the fastest promise in the group is fulfilled

Promise.all()

The returned promise is fulfilled when all promises in the iterable are fulfilled

Returns an array of result values

If any of the promises in the array fail, the returned promise fails too

See the Pen Promise.all() by Liz Shaw (@anythingcodes) on CodePen.

Promise.race()

Instead of waiting for all promises to be fulfilled like in Promise.all(), a promise using Promise.race() is fulfilled as soon as any promise in the array is fulfilled

See the Pen Promise.race() by Liz Shaw (@anythingcodes) on CodePen.

Promise-Based HTTP Requests

function request(url) {
  return new Promise((resolve, reject) => {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onload = function() {
      if (this.status >= 200 && this.status < 300) {
        resolve(JSON.parse(xhr.response));
      } else {
        reject({
          status: this.status,
          statusText: xhr.statusText
        });
      }
    };
    xhr.onerror = function() {
      reject({
        status: this.status,
        statusText: xhr.statusText
      });
    };
    xhr.send();
  });
}

request('http://json.com'); // returns a promise

Fetch

Fetch

The fetch() helper returns a promise and is used for HTTP requests

The initial return value is the Response object; use chaining and the .json() method to get the JSON you need

fetch('http://json.com')
      .then(serverResponse => serverResponse.json())
      .then(json => console.log(json))
      .catch(err => console.log(err));

Let's take a look »

Activity

Using the Fetch Helper

See the Pen Fetch Activity by Liz Shaw (@anythingcodes) on CodePen.

View Solution

Generator Functions

When might you use a generator function?

When there's a distinct transition, e.g. when you're awaiting a response from an external service

When you need to save your place within a function and get back to that specific place later

When you need to pause execution while you get data elsewhere or run some other code

What is it?

Say we want to run some errands at three stores: the grocery store, hardware store, and a cafe. Each time we enter a store, we'll spend time retrieving items.

Shopping trip with generator functions
Shopping trip with generator functions
Shopping trip with generator functions
Shopping trip with generator functions
Shopping trip with generator functions
Shopping trip with generator functions
Shopping trip with generator functions
Shopping trip with generator functions
Shopping trip with generator functions
Shopping trip with generator functions
Shopping trip with generator functions

Each time we visit a store, time passes until we yield some items, then pick up where we left off in our shopping trip

View CodePen »

Making a generator function

  • A generator function returns an iterator
  • Generator functions look like regular functions, but have an asterisks (*) between function and its name:
                function *createIterator() {
  // generator function body here
}

// generator functions are called like regular functions but return an iterator
const iterator = createIterator();
            

Yielding and Pausing

  • Within the generator function, the yield keyword specifies the value to be returned with each iteration
function *createIterator() {
  yield 30;
  yield 31;
  yield 32;
}
  • To return the next yield value, call yourIterator.next() to return an object formatted like { value: yieldValue, done: trueOrFalse}
function *createIterator() {
  yield 30;
  yield 31;
  yield 32;
}

const iterator = createIterator();
console.log(iterator.next()); // { value: 30, done: false }
console.log(iterator.next()); // { value: 31, done: false }
console.log(iterator.next()); // { value: 32, done: false }

// subsequent calls
console.log(iterator.next()); // { value: undefined, done: true }

Why generator functions?

But why?

  1. Can be entered and exited multiple times
    • Pause in the middle, one or many times, and resume later, allowing other code to run during pauses
  2. Pairs well with asynchronous operations
    • Turn asynchronous code into more logical synchronous-looking code
    • Cancel asynchronous operations

Preventing callback hell

Callback Hell
function copyFile (source, dest, callback) {
  fs.exists(source, function (exists) {
    if (!exists) return callback('Source does not exist')
    fs.exists(dest, function (exists) {
      if (exists) return callback('Dest already exists')
      fs.readFile(source, function (err, data) {
        if (err) return callback(err)
        fs.writeFile(dest, data, function (err) {
          return callback(err)
        })
      })
    })
  })
}

Callback hell dog

Generator Functions
const copyFile = watt(function *(source, dest, next) {
  if (!(yield fs.exists(source, next.arg(0)))) throw 'Source does not exist'
  if (yield fs.exists(dest, next.arg(0))) throw 'Dest already exists'
  var data = yield fs.readFile(source, next)
  yield fs.writeFile(dest, data, next)
})
Source: watt library »

 

Let's take a look »

Generator Functions (v5 in Plunkr): A Visual

Chart of an ES6 generator function in an API response chain

 

 

 

 

Iterables

Creating Iterables

By default, objects you create are not iterable

Make them iterable with a [Symbol.iterator] property containing a generator function and for-of loop:

const team = {
  members: ['Sam', 'Tam', 'Lam'],
  [Symbol.iterator]: function *(){
    for (let member of this.members) {
      yield member;
    }
  }
};

Use concise object methods to trim it down further:

const team = {
  members: ['Sam', 'Tam', 'Lam'],
  *[Symbol.iterator](){
    for (let member of this.members) {
      yield member;
    }
  }
};

When would we use iterables?

Parent and child nodes

Iterators with a delivery or project team
Iterators with teams

Let's take a look »

View Solution »

Iterables and Trees

Complex JavaScript node list using iterables

Sets and Maps

Sets and Maps

Set: An ordered list of unique values. Allows fast access to the data it contains.

const set = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
console.log(set.size); // 5
set.add(6);
console.log(set.size); // 6
            

Map: An ordered list of key-value pairs, where the key and value can be any type.

const map = new Map();
map.set('title', 'JS206: Intro to ES6');
map.set('group', 'GDI');

console.log(map.get('title')); // JS206: Intro to ES6
console.log(map.get('group')); // GDI

Built-In Iterators

  • keys() returns an iterator whose values are the keys contained in the collection
  • entries() returns an iterator whose values are key-value pairs
  • values() returns an iterator whose values are the values of the collection

See the Pen Built-in iterators for Sets, Maps, and objects in ES6 by Liz Shaw (@anythingcodes) on CodePen.

Classes

Classes

ES5:

function Vehicle(type) {
  this.type = type;
}

Vehicle.prototype.logType = function() {
  console.log(this.type);
};

var car = new Vehicle('car');
car.logType(); // 'car'

console.log(car instanceof Vehicle); // true
console.log(car instanceof Object); // true

called "creating a custom type"

ES6:

class Vehicle {
  constructor(type) {
    this.type = type;
  }

  logType() {
    console.log(this.type);
  }
}
const car = new Vehicle('car');
car.logType(); // 'car'

console.log(car instanceof Vehicle); // true
console.log(car instanceof Object); // true

 

You don't need commas between the elements of a class

Derived Classes

Derived classes: Classes that inherit from other classes

Within the derived class's class {} definition, use the extends keyword to specify the base class. Then access the base class constructor by calling super().

class Rectangle {
  constructor(length, width) {
    this.length = length;
    this.width = width;
  }

  getArea() {
    return this.length * this.width;
  }
}
class Square extends Rectangle {
  constructor(length) {
    super(length, length); // calls the base class constructor
  }
}

const square = new Square(5);
console.log(square.getArea()); // 25

Activity

Classes and Derived Classes

Activity 1

See the Pen Classes by Liz Shaw (@anythingcodes) on CodePen.

View Solution

Encapsulating Code with Modules

Modules

JavaScript has an error-prone share everything approach to loading code

Modules are individual files that package and encapsulate individual pieces of functionality (variables, functions, and classes)

Export and import only the bindings you need rather than everything in a file

 

🌟 No more global scope pollution!

Exporting

  • Use the export keyword before elements you'd like to expose in a file
// myExports.js
// export variables
export let description = 'GDI Fan';
export const threshold = 7;

// export functions
export function multiply(a, b) {
  return a * b;
}

// export classes
export class Rectangle {
  constructor(length, width) {
    this.length = length;
    this.width = width;
  }
}
// private to the module
function sum(a, b) {
  return a + b;
}

Importing

  • Access the functionality in another module by using the import keyword
  • Each import statement requires:
    1. the functionality you're importing
    2. the module name (typically the file name)
  • Always place import statements at the top of a file
// index.js
import { Rectangle, description, multiply } from './myExports.js';

const shape = new Rectangle(6, 8);
console.log(description); // 'GDI Fan'
console.log(multiply(3, 5)); // 15

Transpiling Setup

🎉 Final Activity

ES6 Transpilation and WebPack Setup

  1. Install Node.js version 6. Make sure it's installed by typing node -v in your favorite command line tool (such as Terminal or X11 on a Mac, or cmd or Git Bash on Windows).
  2. Now get transpiling! Follow along with the instructions at tiny.cc/gdies6-final
  3. Keep the group posted with any roadblocks or solutions you come up with — the first time you transpile can take some time!

 

Transpiling

Additional Resources

Additional Resources

Congrats!

Tina Fey high five congrats gif

Books

 

Keeping in the Loop

Thank you all!