A Beginners' Guide to ES6: Part 2

Hello there again, ES6 Enthusiasts!

Last time, we discussed the fundamentals of ES6, from its variables to basic functions. Now, let's dig a little bit deeper.

Promises

Promises are one of the useful features in ES6. They are used to make async operations such as API requests. Async operations are operations which take some time to complete or is independent to other lines of code we have. 

const apiCall = new Promise(function(resolve, reject) {
   if ( API request to get some data ) {
       resolve("The request is successful and the response is "+ response);
   }
   else {
       reject("The request is not successful. The error is "+error);
   }
});

Promises are defined with a new keyword with a function parameter and has a function parameter resolve and reject. Resolve is called when the async operation returned successful, on the other hand reject when the operation fails.

But how do we manipulate/handle the output returned by the promise? We use handlers. Handlers are functions which executes when an event has occurred, in this case when the resolve or reject function is called.

The handler then is executed:

apiCall.then(function(x) {console.log(x); })
// Output:
// The request is successful and the response is {name: "Jon Snow"}

The handler catch is executed:

apiCall
.then(function(x) {
   console.log(x);
})
.catch(function(x) {
   console.log(x);
})
// Assuming the request is not successful ( reject function is called in the promise. )
// Output:
// The request is not successful

The handler then looks out for the function resolve, so when if the promise returned successful, resolve is called, then then is executed, and the handler catch looks out for the function reject, and in turn is also executed.

Classes

Just like many other features in ES6, classes merely provides us with clean code and syntactic sugar on top of features that already exist. We can define a class using the class keyword, and add a constructor method as well as other instance methods and getters and setters for properties.

class Animal {
   constructor(name, voice) {
       this.name = name;
       this.voice = voice;
       this._eyes = 2;
   }

   get eyes() {
       return this._eyes;
   }

   speak() {
       console.log(`The ${this.name} says ${this.voice}.`);
   }
}
var foo = new Animal('dog', 'woof');
foo.speak(); // The dog says woof.
console.log(`${foo.name} has ${foo.eyes} eyes.`); // dog has 2 eyes

We can create a subclass of a class by using the extends keyword and access parent methods using super.

class Dog extends Animal {
   constructor() {
        super('dog', 'woof');
   }
}
var fido = new Dog();
fido.speak(); // The dog says woof.

We can also attach static methods and properties to the main class so that we can access them without an instance by using the static keyword.

Maps & Sets

ES6 introduces a new data structure called maps. Iterating over a map is also a lot easier than doing so with an object. In ES5, we would loop over the keys and check the values like this:

var obj = {
   'a': 1,
   'b': 2,
   'foo': 7
};
var values = [];

for (var key of obj) {
   values.push(obj[key]);
   if (obj[key] === 7) {
       console.log(key + ' has a lucky value!');
   }
}

Maps are designed to using object literals for holding key-value pairs. It is similar to an array but we can define our own index, and indexes are unique in maps. Now we can easily access all of the keys, all of the values, or both.

var foo = new Map([
   ['a', 1],
   ['b', 2],
   ['foo', 7]
]);

var values = [...foo.values()]; // [1, 2, 7]
var keys = [...foo.keys()]; // ['a', 'b', 'foo']
var entries = [...foo.entries()]; // [['a', 1], ['b', 2], ['foo', 7]]

for (let [key, value] of foo) {
   if (value === 7) {
       console.log(`${key} has a lucky value ${value}!`);
   }
}

Just like Map is a more refined version of using an Object, a Set is a more advanced version of an array.

var sets = new Set();
sets.add('a');
sets.add('b');
sets.add('a'); // We are adding duplicate value.

for (let element of sets) {
   console.log(element);
}

// Output:
// a
// b

Sets are used to store unique values of any type, no duplicate values are displayed even if we intently put a duplicate. Sets are iterable objects, and we have to iterate through the elements to display it.

Async + Await

Async functions and promises are similar since they do not wait for other functions to start or end but work independently from the other codes. The async keyword makes any function to return only promises. When returning some value in the async function, it is equivalent to calling the resolve function. Same is true with reject when getting an error. Since async functions return promises, we can use resolve and reject inside async functions too.

async function hello(a, b) {
   if (a < b) {
       return "Greater";
   }
    else {
       return new Error("Not Greater");
   }
}

hello(14, 10)
.then(function(x) {
   console.log("Good..! " + x);
})
.catch(function(x) {
   console.log("Oops..! " + x);
})

Output:
Oops..! Not Greater. // if you call hello(4, 10) you get "Good..! Greater"

Await can only be used inside an async function, and will not work outside the scope. Await function as the name implies waits until the operation is done. It will make javascript wait until we get a response from an endpoint, then resumes the execution.

async function hello() {
   let response = await fetch('https://api.github.com/');
//fetches the response from the given API endpoint.
   return response;
}

hello()
.then(function(x) {
   console.log(x);
});
...
...
Output:
Response from the API.

Await only halts the execution inside the async function, and the code outside the async function will not be affected, and will still continue. When we get a response, the operation inside the then handler is executed.

Function arguments trailing commas

Trailing commas are now allowed in ES6, it can be used in arrays, objects, parameter definitions, and function calls.

(p,) => {};
Math.max(10, 20,);

var o = {
   p: 42,
   q: true,
};
var {p, q,} = o;

Though it would return an error when calling a function or defining a function parameter containing only a comma. Also, when using rest parameters (…), trailing commas are not allowed.

function f(,) {} // SyntaxError: missing formal parameter
(,) => {};          // SyntaxError: expected expression, got ','

function f(...p,) {} // SyntaxError: parameter after rest parameter

In Conclusion

I know that I had only tackled some of the features of ES6, there is more out there. I just thought that these features are one to take not of and I think will be mostly used. Overall, ES6 has a lot of promising new features than ES5 that I know and certainly will make the life of us devs much easier. But why is it that only some of the browsers support ES6 and not used widely like the ES5? Still, we have to choose the tools we use, and that is what is important, how we use these tools.

Blog Posts by Brian Rex Ortiz