Pankaj Singh

Self-taught Web Developer

JavaScript Symbols, Iterators, Generators, Async/Await, - Explained Simply

Symbols

In ES2015, a new (6th) datatype called symbol was created.

WHY?

The three main reasons were:

Reason #1

Add new core-features with backward compatibility JavaScript developers and the ECMAScript committee (TC39) needed a way to add new object properties without breaking existing methods like for in loops or JavaScript methods likeObject.keys.

For example, if I have an object, var myObject = {firstName:'raja', lastName:'rao'} and if I runObject.keys(myObject) it would return[firstName, lastName] .

Now if we add another property, say newProperty to myObject , and if you run Object.keys(myObject) it should still return old values (that is, somehow make it ignore the newly added newproperty), and show just[firstName, lastName] — and not [firstName, lastName, newProperty] . How to do that?

We couldn’t really do this earlier, so a new data type called Symbols was created.

If you add newProperty as a symbol, then Object.keys(myObject) would ignore this (as it doesn’t know about it), and still return [firstName, lastName] !

Reason #2

Avoid name collisions They also wanted to keep these properties unique. This way they can keep adding new properties (and you can add object properties) to global without worrying about name-collisions.

For example, say that you have an object where you are adding a custom toUpperCase to global Array.prototype .

Now, imagine you loaded another library (or ES2019 came out) and it had a different version of Array.prototype.toUpperCase. Then your function might break because of name collision.

Reason #3

Enable hooks to core methods via “Well-known” Symbols Suppose you want some core function, sayString.prototype.search to call your custom function. That is, ‘somestring’.search(myObject); should call myObject’s search function and pass ‘somestring’ as a parameter! How do we do that?

This is where ES2015 came up with a bunch of global symbols called “well-known” symbols. And as long as your object has one of of those symbols as a property, you can redirect core functions to call your function!

We can’t talk much about this right now, so I’ll go into all details a bit later in this article. But first, let’s learn about how Symbols actually work.

Creating Symbols

You can create a symbol by calling a global function/object called Symbol . That function returns a value of datatype symbol.


Symbols can’t be created by “new” keyword

Because symbols are not objects and the new keyword is supposed to return an Object, we can’t use new to return a symbols datatype.

var mySymbol = new Symbol(); //throws error

Symbols have “description”

Symbols can have a description — it’s just for logging purposes.

//mySymbol variable now holds a "symbol" unique value //its description is "some text"
const mySymbol = Symbol('some text');

Symbols are unique

const mySymbol1 = Symbol('some text');
const mySymbol2 = Symbol('some text');
nmySymbol1 == mySymbol2 // false

Symbols behave like a singleton if we use “Symbol.for” method

Instead of creating a symbol via Symbol() , you can create it via Symbol.for(). This takes a “key” (string) to create a Symbol. And if a symbol with that key already exists, it simply returns the old symbol! So it behaves like a singleton if we use the Symbol.for method.

var mySymbol1 = Symbol.for('some key'); //creates a new symbol var mySymbol2 = Symbol.for('some key'); // **returns the same symbol
mySymbol1 == mySymbol2 //true

Symbol’s “description” versus “key”

Just to make things clearer, if you don’t use Symbol.for , then Symbols are unique. However, if you use it, then if your key is not unique, then the symbols returned will also be not unique.


Symbols can be an object property key

This is a very unique thing about Symbols — and also most confusing. Although they appear like an object, they are primitives. And we can attach a symbol to an Object as a property key just like a String.



Iterators and Iterables

WHY?

In almost all our apps, we are constantly dealing with lists of data and we need to display that data in the browser or mobile app. Typically we write our own methods to store and extract that data.

But the thing is, we already have standard methods like the for-of loop and spread operator (…) to extract collections of data from standard objects like arrays, strings, and maps. Why can’t we use these standard methods for our Object as well?

In the example below, we can’t use a for-of loop or spread operator to extract data from our Users class. We have to use a custom get method.


But, wouldn’t it be nice to be able to use these existing methods in our own objects? In order to achieve this, we need to have rules that all developers can follow and make their objects work with existing methods.

If they follow these rules to extract data from their objects, then such objects are called “iterables”.

The rules are:

  1. The main object/class should store some data.
  2. The main object/class must have the global “well-known” symbol symbol.iterator as its property that implements a specific method as per rules #3 to #6.
  3. This symbol.iterator method must return another object — an “iterator” object.
  4. This “iterator” object must have a method called the next method.
  5. The next method should have access to the data stored in rule #1.
  6. And if we call iteratorObj.next(), it should return some stored data from rule #1 either as {value:, done: false} format if it wants to return more values, or as {done: true} if it doesn’t want to return any more data. If all those 6 rules are followed, then the main object is called as an “iterable” from rule #1. The object it returned is called an “iterator”.

Let’s take a look at how we can make our Users object and iterable:


Generator functions

WHY?

There are two main reasons:

  1. provide higher-level abstraction to iterables
  2. Provide newer control-flow to help with things like “callback-hell”.

Generator syntax and usage

Generator functions can be used in the following ways:

ASYNC/AWAIT

WHY?

As you saw earlier, Generators can help eliminate “callback hell”, but you need some 3rd party library like co to make that happen. But “callback hell” is such a big problem, the ECMAScript committee decided to create a wrapper just for that aspect of Generator and came out with the new keywords async/await.

The differences between Generators and Async/Await are:

  1. async/await uses await instead of yield.
  2. await only works with Promises.
  3. Instead of function*, it uses the async function keyword.

SUMMARY

Symbols

— provide a globally unique data type. You use them mainly as object properties to add new behaviors so you don’t break standard methods like Object.keys and for-in loops.

Iterables 

— are any objects that store a collection of data and follow specific rules so that we can use standard for-of loop and ... spread operators to extract data from within them.

Iterators 

— are returned by Iterables and have the next method — it’s what actually extracts the data from an iterable.

Generators

—provide higher level abstraction to Iterables. They also provide new control-flows that can solve things like callback-hell and provide building blocks for things like Async/Await.

Async/Await 

— provides higher level abstraction to Generators in order to specifically solving callback-hell issue.

Article By Pankaj Singh

Skilled in programming language JavaScript, comfortable with the latest versions ES6. Also having good Python knowledge with strong OOPs concept. Full Stack Developer using NodsJs/Django for Backend with CSS/HTML/JavaScript/MDBootstrap, API, MySQL,MongoDB,Github and Herokuapp. Analytical thinker that consistently resolves ongoing issues or defects, often called upon to consult on problem that have eluded resolution from others.

Discuss about post

Subscribe to my weekly newsletter

Read Also

How to make your company website based on bootstrap framework...