Categories
Modern JavaScript

Best Features of ES2017 — Async Functions

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the best features of ES2017.

Async Functions

Async functions is one big feature released with ES2017.

It’s a useful shorthand for the then method.

We can declare async functions in the following ways:

  • async function declarations: async function foo() {}
  • async function expressions: const foo = async function () {};
  • async method definitions: let obj = { async foo() {} }
  • async arrow functions: const foo = async () => {};

Async functions always return promises.

So if we have:

async function asyncFunc() {
  return 'foo';
}

then asyncFunc returns a promise that resolves to 'foo' .

Then we can use it by writing:

asyncFunc()
  .then(x => console.log(x));

To reject a promise, we throw an error:

async function asyncFunc() {
  throw new Error('error');
}

Then we can catch the error by writing:

asyncFunc()
  .catch(x => console.log(x));

We can handle results and errors of promises with await .

The await operator is only allowed in async functions.

It waits for its operand, which is always a promise to be settled.

If the promise is fulfilled, then the result of await is its fulfillment value.

If the promise is rejected, then await throws the rejection value.

So we can write:

async function asyncFunc() {
  const result = await promise;
  console.log(result);
}

where promise is the promise we’re waiting for the result of.

That’s the same as:

async function asyncFunc() {
  return promise
    .then(result => {
      console.log(result);
    });
}

The good thing with this syntax is that we can handle multiple promises in a shorter way.

So we can write:

async function asyncFunc() {
  const result1 = await promise1();
  console.log(result1);
  const result2 = await promise2();
  console.log(result2);
}

That’s the same as:

function asyncFunc() {
  return promise1()
    .then(result1 => {
      console.log(result1);
      return promise2();
    })
    .then(result2 => {
      console.log(result2);
    });
}

As we can see, it’s much shorter.

To run multiple promises in parallel, we can use Promise.all :

async function asyncFunc() {
  const [result1, result2] = await Promise.all([
    promise1(),
    promise2(),
  ]);
  console.log(result1, result2);
}

This is the same as:

async function asyncFunc() {
  return Promise.all([
      promise1(),
      promise2(),
    ])
    .then(([result1, result2]) => {
      console.log(result1, result2);
    });
}

To handle errors, we use try-catch as we do with synchronous functions:

async function asyncFunc() {
  try {
    await promiseFunc();
  } catch (err) {
    console.error(err);
  }
}

This is the same as:

function asyncFunc() {
  return promiseFunc()
    .catch(err => {
      console.error(err);
    });
}

Async function works by using generators to wait for the result until it continues execution.

It waits for the result each time the await operator is added.

Once the promise is fulfilled, then the async function continues to run.

Async functions are started synchronously and settled asynchronously.

The result of an async function is always a promise.

The promise is created when the async function starts.

The body of it is then run.

It may finish with return or throw .

Or it may be paused with await and continues once the result is obtained.

Then finally, the promise is returned.

Conclusion

Async functions is a great shorthand for writing promise code.

These are functions that always return promises.

Categories
Modern JavaScript

Best Features of ES2016

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the best features of ES2016.

Array.prototype.includes

The includes array instance method is added in ES2016.

It lets us check if an item exists in the array.

For instance, we can write:

[1, 2, 3].includes(1)

then that returns true .

If we have:

[1, 2, 3].includes(6)

then it returns false .

It takes the item we want to check and returns a boolean.

includes is similar to indexOf .

So:

arr.includes(x)

is mostly the same as:

`arr.indexOf(x)` `>=` `0`

The main difference is that includes can check for NaN , but indexOf can’t.

So if we write:

[NaN].includes(NaN)

then that returns true .

But if we have:

[NaN].indexOf(NaN)

then that returns -1.

includes doesn’t distinguish between +0 and -0.

So if we write:

[-0].includes(+0)

then that returns true .

Exponentiation operator (**)

The exponentiation operator is added to ES2016.

It lets us do exponentiation without calling Math.pow .

For instance, if we have:

3 ** 2

we get 9.

So:

`x` `**` `y`

does the same thing as:

Math.pow(x, y)

We can use ** with = .

So we can write:

let num = 3;
num **= 2;

then num is 9.

Precedence

The exponentiation operator binds very strongly.

So if we have:

2**2 * 2

then that’s the same as:

(2**2) * 2

It binds more strongly than * .

And * binds more strongly than ** .

Conclusion

ES2016 is a small update to ES2015, which was a big release.

It gives everyone a break on new features for a year.

New features include the array instance includes method and the exponentiation operator.

Categories
Modern JavaScript

The Best Features of JavaScript to Date

JavaScript has improved immensely as a language. Now we can actually use it as a general-purpose programming language.

In this article, we’ll look at the best feature of JavaScript to date.

let and const

Without question, let and const let us create variables without hassle unlike var or declaring variables with no keywords.

let lets us declare block-scoped variables.

const lets us declare block-scoped constants that can’t be reassigned a new value.

This is good since now we don’t have to worry about function scoped variables with var and hoisting.

We’ll get an error if we declare constants with const and try to reassign it.

Arrow Functions

Arrow functions are great. They let us create functions that can only be used after they’re defined.

With it, we don’t have to worry about the value this inside the function.

We just have to worry about what we do with the function.

It’s also shorter. We can define it with the following code:

const subtract = (a, b) => a - b;

It’s just easier for us to deal with when we’re writing callbacks.

this is inherited from the outside, so it’s perfect for nested functions and callbacks.

For instance, if we have the following:

class Timer {
  startTimer() {
    this.foo = 'foo';
    setInterval(() => console.log(this.foo), 1000)
  }
}

Then we see foo logged in the console every second since this.foo ‘s value is 'foo' , and the arrow function callback takes this straight from the outside.

Class Syntax

Writing constructor functions is hard, especially if we need to have instance methods and inheritance.

The class syntax solves the issues with the confusing constructor syntax.

Instead of writing constructor functions as follows:

function Person(name) {
  this.name = name;
}

Person.prototype.greet = function(greeting) {
  return `${greeting} ${this.name}`
}

We write:

class Person {
  constructor(name) {
    this.name = name;
  }

  greet(greeting) {
    return `${greeting} ${this.name}`;
  }
}

It’s much cleaner than the old constructor syntax since we don’t have to add methods to the prototype property of the object.

Promises / Async functions

Promises become a built-in feature of JavaScript since ES2015. It’s great because we can use it to write complex async code without nesting callbacks everywhere.

All we have to do is to return promises in and call then to chain promises to write our serial async code.

For instance, we can have:

Promise.resolve(1)
  .then(() => Promise.resolve(2))
  .then(() => Promise.resolve(3))

Async functions make things even shorter and they do the same thing as chaining promises.

For instance, we can shorten the code above to:

(async () => {
  await Promise.resolve(1);
  await Promise.resolve(2);
  await Promise.resolve(3);
})()

We reduce nested by eliminating the callback. All lines return the resolved value of each promise.

So if we have:

(async () => {
  const val = await Promise.resolve(1);
  console.log(val);
})()

We get 1 for val since that’s its resolved value.

The code above is the same as:

Promise.resolve(1)
  .then(val => console.log(val));

async functions can also return a value, which will return a promise that resolves to that value.

So if we write:

(async () => {
  return 1;
})()

We get that 1 is the resolved value of the promise returned by that function.

Modules

Modules are great since they replace script tags. Script tags weren’t good because they’ve to be loaded in order and variables and functions are called out of nowhere.

It’s been a built-in feature since ES2015.

We can import the items from modules and use them instead. Then we know where everything comes from.

Also, we can hide items without wrapping them in functions and returning what we want.

This is much more convenient than ordering script tags and having long functions when we create a big app.

With modules, we can organize code as we do in most other programming languages.

Conclusion

JavaScript has improved a lot. If we didn’t use then features listed above in our app, then we should use them now.

There is just no way that we can live without these features which are in many programming languages before they made it to JavaScript.

We got to use them wherever we can and make all new JavaScript projects incorporate these features.

Categories
Modern JavaScript

Best of Modern JavaScript — Resolve and Reject Promises

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at JavaScript promises.

Other Ways of Creating Promises

Other than using the Promise constructor, we can create promises in other ways.

One way is the Promise.resolve method.

It takes the resolved value of the promise as its argument.

And it returns a promise.

For instance, we can write:

Promise.resolve(1)  
  .then(x => console.log(x));

x is the resolved value from Promise.resolve .

If x is a promise whose construction is the receiver, then the promise is unchanged.

For instance, if we have:

const p = new Promise(() => null);  
console.log(Promise.resolve(p) === p);

then we get true from the console log.

If the argument is a thenable and then the then method in the argument is a function, then the resolved value with Promise.resolve is the argument that we call the then parameter with.

For example, if we have:

const thenableObj = {  
  then(reaction) {  
    reaction('foo');  
  }  
};  
const promise = Promise.resolve(thenableObj);  
console.log(promise instanceof Promise);   
promise.then(x => console.log(x));

We have a thenableObj , which has the then method.

It takes a reaction function and we called it with 'foo' .

Then we pass in an object to the Promise.resolve method, which returns a promise.

If we check if promise is an instance of a Promise , then that returns true .

We can also call then on it with a callback to get the 'foo' value, which is assigned to x .

Promise.reject()

We can call Promise.reject() to return a promise that’s rejected with a value.

For example, we can write:

const error = new Error('error');  
Promise.reject(error)  
  .catch(err => console.log(err === error));

We pass in an error object by passing that into the Promise.reject method.

Then we call catch by passing in a callback into it.

err has the error object that we called Promise.reject with, which should be the same as error .

Chaining Promises

We can chain promises with then as long as we return a promise.

Whatever we return in the then callback will be the resolved value of the promise.

For instance, if we have:

Promise.resolve('bar')  
  .then(function(value1) {  
    return 'foo';  
  })  
  .then(function(value2) {  
    console.log(value2);  
  });

Then value2 would be 'foo' .

This lets us flatten promises chains.

Instead of writing:

Promise.resolve('bar')  
  .then(function(value1) {  
    Promise.resolve('foo')  
      .then(function(value2) {  
        //...  
      });  
  })

We write:

Promise.resolve('bar')  
  .then(function(value1) {  
    return 'foo';  
  })  
  .then(function(value2) {  
    //...  
  });

Catching Errors

We can catch errors with the catch method.

For example, we can write:

Promise.reject(new Error('error'))  
  .catch(function() {  
    return 'error occurred';  
  })  
  .then(function(value) {  
    //...  
  });

We called catch to catch the error from the rejected promise and run our own code.

This lets us set some value for the next promise to use.

Throw an Exception

We can throw an exception in the then callback, then the promise returned will be rejected.

For instance, we can write:

Promise.resolve()  
  .then(function(value) {  
    throw new Error();  
  })  
  .catch(function(reason) {  
    // ...  
  });

We threw an error in the then callback, so the catch callback will run.

Conclusion

We can resolve a promise with a value so the next one can be invoked.

Also, we can reject promises and catch the error with catch .

Categories
Modern JavaScript

Best of Modern JavaScript — Proxy Handlers

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at how to control JavaScript object operations.

Function Proxy Handlers

We can also add handler methods to the handler we pass into the Proxy constructor or Proxy.revocable method.

apply(target, thisArgument, argumentsList) lets us change the behavior of the apply or call method.

It can also change the behavior of direct calls to a function.

target is the target object which we want to control.

thisArgument lets us pass in the value of this in the function.

argumentsList lets us pass in a list of arguments.

Invariants of Handler Methods

Handler methods have various invariants enforced on them.

They include the following.

apply(target, thisArgument, argumentsList) has no invariants enforced on them.

construct(target, argumentsList, newTarget) must return an object, and not null or a primitive value.

defineProperty(target, propKey, propDesc) must not let us add [properties to an object that isn’t extensible.

The target must have a property must have a non-configurable own property with propKey if configurable is false .

If writable and configurable are both false , then we can’t write a new property to the property.

deleteProperty(target, propKey) can’t delete non-configurable own properties of the target .

get(target, propKey, receiver) must return the property’s value if it has an own, non-writable, and non-configurable data property with name propKey .

Otherwise, it must return undefined .

getOwnPropertyDescriptor(target, propKey) must return either an object or undefined .

Non-configurable now properties of the target can’t be reported as non-existent.

If the target is non-extensile, then it must report as existing.

It can’t report a non-configurable property as configurable and can’t report a different value for a non-configurable and non-writable property.

getPrototypeOf(target) must return either an object or null .

If the target isn’t extensible, then the handler must return the prototype of the target object.

has(target, propKey) mustn’t hide a non-configurable own property of target .

If the target is on-extensible than no own property of target may be hidden.

isExtensible(target) must return a boolean.

The coerced boolean value must be the same as target.isExtensible .

ownKeys(target) must return an object which is treated as array-like and converted to an array.

Each element of the result must either be a string or a symbol.

The result must have the keys of all non-configurable non-inherited properties of target .

If target isn’t extensible then the result must only the keys of its own properties.

preventExtensions(target) returns a boolean.

target.isExtensible() must return false after this is called.

set(target, propKey, value, receiver) should set the property of a writable, configurable property of target .

If target already has an own non-configurable property with name propKey , then a TypeError should be thrown since the property can’t be set.

setPrototypeOf(target, proto) ‘s returned result is coerced into a boolean.

If the target isn’t extensible, then its prototype can’t be changed.

In this case, target ‘s prototype must be the same as what it is now, or a TypeError would be thrown.

Conclusion

The proxy handler methods must follow some conditions even though it lets us customize those operations.

This way, the object operations are still predictable.