Categories
Modern JavaScript

Best Features of ES2017 —Async Functions Pitfalls

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.

Properties of Async Functions

Async functions don’t wrap promises that we return.

For instance, if we have:

async function asyncFunc() {
  return Promise.resolve('foo');
}

Then if we have:

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

Then x is 'foo' .

If we return rejected promise, then the thrown error value will be in the catch callback.

For instance, we can write:

async function asyncFunc() {
  return Promise.reject(new Error('error'));
}

Then if we write:

asyncFunc()
  .catch(err => console.error(err));

and error is the Error instance.

Async Function Tips

We should never forget await if our code on the right side of the = is a promise.

For example, we shouldn’t write:

async function asyncFunc() {
  const value = promiseFunc();
  //...
}

That won’t work since there’s no await to wait for the result of the promise.

await makes sense even if the function doesn’t return anything.

We can use the promise a signal to pause the function until something is done.

For instance, we can write:

async function asyncFunc() {
  await sleep(2000);
  //...
}

to make the function pause for 2 seconds with our own sleep function.

We can implement sleep however we like to as long as it returns a promise.

We Don’t Always Need await

If we just want to invoke a promise and don’t want to wait for its result, then we don’t need to add await to it.

For instance, we can write:

async function asyncFunc() {
  const writer = openFile('foo.txt');
  writer.writeLine('foo');
  writer.writeLine('bar');
  await writer.close();
}

We didn’t add await to the writer.writeLine method calls because we don’t want to wait for the result.

This way, we can make our function run faster since we don’t have to wait.

await is sequential, Promise.all() is parallel

We’ve to remember that await is run sequentially.

And Promise.all runs the array of promise we pass in in paralle.

So if we have:

async function foo() {
  const result1 = await promise1();
  const result2 = await promise2();
}

then await waits for each async function is fulfilled until the next line is run.

On the other hand, if we have:

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

then promise1 and promise2 are run in parallel.

Async Functions and Callbacks

await only affects the surrounding async function.

So an async function can’t await in a callback.

However, callbacks can be async functions.

This makes callbacks tricky to use.

For instance, we can’t use await in a map callback that isn’t async:

async function download(urls) {
  return urls.map(url => {
    const content = await makeRequest(url);
    return content;
  });
}

We don’t have an async keyword in front of the function.

But we also can’t write:

async function download(urls) {
  return urls.map(async url => {
    const content = await makeRequest(url);
    return content;
  });
}

since we won’t be able to get the results of each async function from the callback.

The awaiting is only done inside the callback.

What we should do is to map our URLs to promises and then call Promise.all on the array of promises.

For instance, we write:

async function download(urls) {
  const promiseArray = urls.map(async (url) => {
    const content = await makeRequest(url);
    return content;
  });
  return await Promise.all(promiseArray);
}

We call map to map the array of URLs to an array of promises, and then we return our Promise.all call to return a promise that resolves to the results of all the promises in an array.

Conclusion

Async functions are handy to use, but it can be misused if we aren’t careful.

So we should make sure to use them properly to avoid unexpected results.

Categories
Modern JavaScript

Best Features of ES2017 — Async Functions and Arrays and Shared Buffers

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 and Array.prototype.forEach()

Array.prototype.forEach doesn’t work with the async and await syntax.

For instance, if we have:

async function downloadContent(urls) {
  urls.forEach(async url => {
    const content = await makeRequest(url);
    console.log(content);
  });
}

then we won’t get all the results of the promises because forEach doesn’t wait for each promise to finish.

Instead, we want to use the for-of loop to iterate through each async function to get our result:

async function downloadContent(urls) {
  for (const url of urls) {
    const content = await makeRequest(url);
    console.log(content);
  }
}

The for-of loop is aware of the await operator so we can use it loop run all the async functions sequentially.

If we want to run the async functions in parallel, we can use Promise.all :

async function downloadContent(urls) {
  await Promise.all(urls.map(
    async url => {
      const content = await makeRequest(url);
      console.log(content);
    }));
}

We mapped the URLs to async functions so that we can call Promise.all on the array of promises.

And we return a promise with the resolved value being an array of the promises’ resolved values.

Immediately Invoked Async Function Expressions

We can create async functions that are run immediately.

For instance, instead of writing:

async function foo() {
  console.log(await promiseFunc());
}
foo();

We can write:

(async function () {
  console.log(await promiseFunc());
})();

It can also be an arrow function:

(async () => {
  console.log(await promiseFunc());
})();

Unhandled Rejections

We don’t have to worry about unhandled rejections when we use async functions.

This is because browsers report them to us when we encounter them.

For instance, we can write:

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

Then we’ll see the error logged in the console.

Shared Array Buffers

ES2017 introduced shared array buffers which lets us build concurrent apps.

They let us share the bytes of a SharedArrayBuffer object between multiple workers and the main thread.

The buffer us shared and is wrapped in a typed array so we can access them.

We can share data between workers quickly and coordination between workers is simple and fast.

For instance, we can create a shared array buffer by writing:

const worker = new Worker('worker.js');

const sharedBuffer = new SharedArrayBuffer(
  100 * Int32Array.BYTES_PER_ELEMENT);

worker.postMessage({
  sharedBuffer
});

const sharedArray = new Int32Array(sharedBuffer);

We created a worker in worker.js .

Then we created a shared buffer with the SharedArrayBuffer .

It can contain 100 elements.

Then to share the buffer with the worker, we call postMessage to pass the buffer to the worker,.

To access the buffer’s data, we create a new Int32Array instance.

Then in the worker.js worker, we get the buffer by writing:

self.addEventListener('message', (event) => {
  const {
    sharedBuffer
  } = event.data;
  const sharedArray = new Int32Array(sharedBuffer);
  //...
});

We listen to the message event and get the sharedBuffer property of event.data .

Then we can access it the same way.

Conclusion

Async functions don’t work well with existing array instance methods.

Also, we can use shared array buffers to share data between the main and worker threads.

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.