ES6: Return of Javascript

Quest Henkart
5 min readJun 9, 2015

--

This is the third and final installment of my blog trilogy on ES6 based on the information presented by Aaron Frost on Pluralsight. If you haven’t seen the talk, check it out! In this episode, we are going to take a glance at promises, generators, and modules!

Promises

Promises are one of the most exciting features being presented in ECMAscript6. If anyone has ever worked heavily in asynchronous code, then you are probably very familiar with Callback Hell, let me show you what it looks like:

importThis(function(){ 
importThat(function(){
doSomethingElse(function(){
waitForEvent(function(){
respondWithSomething(function(){
save(function{
moveOn();
})
})
})
})
})
})

Callback Hell commonly occurs during chained asynchronous functionality. Without setting the callback properly, the next step would get called before the previous step completed, usually causing an error or crashing the program.

Promises come to the rescue by offering an agreement to the app that promises to execute once the previous call has finished executing, it allows us to completely avoid callback hell and makes code run a lot more efficiently. It works by creating an event handler, and when it recieves a result or response, fires the event so that it gets added to the event loop.

Promise Constructor

var promise = new Promise(function(resolve, reject) {}

The promise takes a resolve which is a callback function that is executed upon success and a reject that is executed upon failure. The promise object that is returned from the constructor can be in 1 of 4 states:
1. fulfilled: successfully resolved
2. rejected: rejected
3. pending: still working, neither resolved or rejected
4. settled: fullfilled or rejected

var promise = new Promise(function(resolve, reject){ 
if(success()){
resolve("success");
}else {
reject("Failure");
}
});

No matter how long this takes to run, the following code will only be executed upon its completion, and without locking up the program.

promise.then(
function(result){
console.log(result); // "success"
},
function(err){
console.log(err); // Error: "Failure"
}
})

Promises can also be simplified by wrapping it into an outer function, here is a use case with an asynchronous function in jQuery

function get(url){ 
return new Promise(function(resolve, reject){
$.get(url, function(data){ //jquery async method
resolve(data);
})
.fail(function(){
reject();
})
})
}
get('users.all').then(
function(users){
app.users = users;
},
function(){
console.log("users not found")
}
})

Promises.all

The new ES6 promises also offer a feature that waits until all currently pending promises are complete before continuing. This is perfect for complex API or Database calls.
utilizing the get method from the previous example

var usersPromise = get('users.all'); 
var postsPromise = get('posts.everyone');
Promise.all([usersPromise, postsPromise]).then(
function(results){
controller.users = results[0];
controller.posts = results[1]
},
function(){
console.log(users and posts not found)
}
);

When returning items in a promise, the return statement gets passed to the next .then

get('users.all').then(function(usersString) {
//returns the statement to the next .then in the promise
return JSON.parse(usersString);
})
.then(function(users){
//using the previously parsed information here
app.users = users;
})

Promise Methods

  1. Promise.all(iterable); : waits until all pending promises are complete
  2. Promise.race(iterable); : waits until 1 promise is complete even if others are pending
  3. Promise.reject(reason); : creates a pre-rejected promise
  4. Promise.resolve(value); : Create a pre-resolved promise

Generators

Generates offer an awesome way to control the flow of a program. It offers the yield keyword, that allows a function or process to pause mid-process to see if there are any items waiting in the event loop such as an event handlers that has been called

function *foo(){ 
yield 1;
yield 2;
return 3;
}
var gen = foo();
gen.next(); //returns {value: 1, done:false}

The * signifier sets a function up as a generator, when calling gen.next(), an object is returned, the first key in the object is the value, which is the return statement provided by the initial yield keyword, the second key is done, which will be false if there are other yields in the function, or true if there are no more yield statements left.
Now we call .next() again

gen.next(); // returns {value: 2, done: false} //there are no more //yield statements, so done now equals true
gen.next(); // returns {value: 3, done: true}
//there are no more values so it returns undefined
gen.next(); // returns {value: undefined, done: true}

Iterators

The Generator contains an iterator so we can use the for of loop. If you are unfamiliar with iterators, check out my last blog post here. It is very important to note, that only the yield statements are iterable. The iterator will operate as long as done does not equal true, and done equals true once there are no more yield statements.

function *foo(){ 
yield 1 yield 2 return 3
}
for (var val of foo()){
console.log(val)
} // 1, 2

Passing in Values

function *foo(x){ 
var y = 2 * (yield(x+1));
var z = yield (y/3);
return (x + y + z);
}
var gen = foo(5)
console.log(gen.next()); // {value: 6, done: false}
console.log(gen.next(12)); // {value: 8, done: false}
console.log(gen.next(13)); // {value: 42, done: true}

Pretty cool huh!? Let’s walk through this problem step by step so we can gain a deep understanding of what is happening here.

var gen = foo(5) sets the initial x === 5

So let’s rewrite the first line with the new variable applied

function *foo(5) { 
var y = 2 * (yield(5+1));
}

next we have the first yield, no value is applied

var y = 2 * 6 // the first next executes yield(5+1), which returns {value: 6, done: false}

The app stays at this state until the next .next is applied. This yield comes with a variable of 12 which replaces the previous yield’s value

function *foo(5){ 
y = 2*12 //y = 24
var z = (24/3) // y is applied and yield(24/3) returns {value: 8,
//done: false}
}

the final .next is called this time with a variable of 13, just like before, the 13 replaces the previous yield.

Finally we have three variables left, from all the .next parameters passed in.

var x = 5 
var y = 24
var z = 13
return x + y + z //{value:42, done: true }

As you can see, there is a ton of power in yielding and passing values. It gives us enormous control over our functions and the way we run applications.

Generator Expressions were pushed to ES7

Modules

ES6 brings in some much needed support for modules. Those who have worked with Node or are familiar with CommonJS will not find anything surprising here. However getting modules working out of the box in javascript is going to be an incredible feature and make the use of front-end frameworks significantly cleaner. If you’ve ever tried using jQuery or some tool made with jQuery within the Angular framework, you probably ran into a huge problem. The $ sign which is jQuery’s main signifying tag is also heavily used in Angular. There are work arounds, but with modules, these workarounds will no longer be necessary. Let’s dive in!

Explicit Imports

file.js: 
export var test;
export function sqr(x){console.log(x*x)};
export var test3;
main.js:
import{test, test2} from 'file';
sqr(3); // 9

Import All

main.js:

import * as lib from 'file';
lib.sqr(3); // 9

Export or import aliases

function sqr(x){
console.log(x*x)
}
export {sqr as squareLog}

Or

import {sqr as squareLog} from 'file'; 
squareLog(3); // 9

When can I use ES6 ???

You can check out this page here, which tracks which browser and repl has adopted the various features of ES6. Although, realistically you should be using Babel to transpile your ES6 code for any browser

Originally published at questhenkart.com on June 9, 2015.

--

--