Categories
JavaScript Best Practices

JavaScript Best Practices — Promises

Some ways of writing JavaScript code are better than others.

In this article, we’ll look at best practices for using the latest JavaScript promise features.


Converting Non-Promise Async Code to Promises

We can convert the non-promise async code to promises with the util.promisify() method. This lets us convert many functions with async callbacks with the signature of the form (err, res), where err is an object with errors and res has the results of the async code.

Any function that has a promising form defined for it can also use this utility to return the promise form. As long as the promise version is stored as the value of the symbol property util.promisify.custom , we can get the promise version.

For instance, since setTimeout has a promise-based version in the standard library of Node.js, we can convert it to a promise as follows:

const util = require('util');
const sleep = util.promisify(setTimeout);

(async () => {
  await sleep(1000);
  console.log('slept');
})()

Now we have a sleep function that pauses execution for the time we want, instead of having to nest callbacks.


Asynchronous Chaining

In the old way, we chain promises with the then method.

For instance, we write:

promise1
  .then((res) => {
    //...
    return promise2
  })
  .then((res) => {
    //...
    return promise3
  })
  .then((res) => {
    //...
  })

We can clean this up with async and await as follows:

(async () => {
  const val1 = await promise1;
  //...
  const val2 = await promise2;
  //...
  const val3 = await promise3;
  //...
})();

This is much cleaner as the resolve promise values are assigned to the constants or variables on the left side.


Using Catch to Catch Errors

As with synchronous code, we have to catch errors and handle them gracefully.

To do that with promise code, we can call the catch method. For instance:

promise1
  .then((res) => {
    //...
    return promise2
  })
  .then((res) => {
    //...
    return promise3
  })
  .then((res) => {
    //...
  })
  .catch((err) => {
    //...
  })

If we use async and await , then we use try...catch as we do with synchronous code, as follows:

(async () => {
  try {
    const val1 = await promise1;
    //...
    const val2 = await promise2;
    //...
    const val3 = await promise3;
    //...
  } catch (ex) {
    //...
  }
})();

The code in catch in both examples would run when the first promise is rejected.


Use Finally to Run Code That Should Run Regardless of the Outcome of the Promises

If we need to run code that should be run regardless of the outcomes of the promise chain, then we should use the finally method or the finally block for async and await code.

For instance, we can run clean up code in finally as follows:

promise1
  .then((res) => {
    //...
    return promise2
  })
  .then((res) => {
    //...
    return promise3
  })
  .then((res) => {
    //...
  })
  .finally((err) => {
    //...
  })

Or we can write:

(async () => {
  try {
    const val1 = await promise1;
    //...
    const val2 = await promise2;
    //...
    const val3 = await promise3;
    //...
  } catch (ex) {
    console.log(ex);
  } finally {
    //...
  }
})();

Multiple Asynchronous Calls with Promise.all()

If we want to run multiple unrelated promises at once, then we should use Promise.all.

For instance, we can run them all at once as follows:

Promise.all([
    promise1,
    promise2,
    promise3,
  ])
  .then((res) => {
    //...
  })

If we use async and await, we can write:

(async () => {
  const [val1, val2, val3] = await Promise.all([
    promise1,
    promise2,
    promise3,
  ])
})();

In both examples, the resolved values of all three promises are either in res or assigned to an array on the left (in the second example).

This way, they run all at once instead of waiting for each to resolve.


Summary

  • We can clean up our async code by using promises.
  • Converting to async code to promises can be done with Node apps by using the util.promisify method.
  • If we want to chain promises, before sure to add the catch method or block to handle errors gracefully.
  • finally method or block can be used to run code that’s always run regardless of the promises outcomes.
  • Promise.all is great for running unrelated promises all at once.
Categories
JavaScript Best Practices

JavaScript Best Practices — Modern Syntax

There are better ways to write JavaScript code than others.

In this article, we’ll look at the best practices for using the latest JavaScript syntax features.

Syntactic Sugar

In JavaScript, the syntactic sugar that’s included is usually good.

They let us write code in a shorter and clear way and make them more readable in the process.

These syntactic sugars are drop-in replacements for existing techniques so we should use them.

const Isn’t Consistent

const is a keyword for declaring block-scoped constants.

They’ll make primitive values assigned to it immutable since we can’t assign a constant again after it’s assigned to a value.

A value must be assigned when we declare it.

However, const is a bit deceptive since some people might not know that we can still change objects that are assigned to const by changing their properties.

Also, arrays can be changed in place with methods like push and unshift .

Therefore, we shouldn’t assume that objects that are assigned to const are immutable.

Limiting the Scope of the Function

Traditional functions defined with the functionm keyword can be called to run statements defined in the block and may return a value.

They can be run anywhere if they’re written as a function declaration.

For instance, if we have:

function foo() {
  //...
}

Then foo can be run before or after it’s defined.

It also defines its own this and can be used with the new operator as a constructor.

So, to limit the capabilities of our functions, we should use arrow functions.

If we need constructors, then we should use the class syntax to define them to make everyone clear.

The Class Syntax

The class syntax is great for defining constructors. It does the same thing as the old constructor function syntax.

It looks like a class in an object-oriented language like Java, but it does the same thing as JavaScript constructors.

Therefore, the prototypical inheritance model is still used with JavaScript classes.

So the following:

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

Person.prototype.greet = function() {
  console.log(`hi ${this.name}`);
};

is the same as:

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

  greet() {
    console.log(`hi ${this.name}`);
  }
}

They hold and do the same thing but the placement of the fields and methods are different.

Arrow Functions

Arrow functions are great. They are shorter. Thet but more neatly in callbacks.

It can return without adding the return keyword if we return in the first line.

They encapsulate qualities that make them more convenient. But they aren’t a drop-in replacement for traditional functions defined with the function keyword.

We can call bind by to change this inside a function.

Also, we can’t call call and apply with them to change this and call functions with arguments with them.

Going Asynchronous

Going async with JavaScript has its own difficulty because of JavaScript’s single-threaded nature.

We’ve to write code that unblocks the thread so that they won’t hold up our programs until we’re ready to run them.

This is where async code with callbacks come in to make calling async code easier.

We can make our code async by calling setTimeout . Also, pretty much any HTTP client runs in an async manner.

If we have lots of async code with callbacks, then we have to nest callbacks.

This gets ugly real fast.

If we use callbacks for chaining async code, we may have code that looks like this:

async1((err, res) => {
  if (!err) {
    async2(res, (err, res) => {
      if (!err) {
        async3(res, (err, res) => {
          //...
        });
      }
    });
  }
});

That’s ugly and just not maintainable. Instead, we use promises. Then we can write something like:

promise1
  .then((res) => {
    //...
    return promise2
  })
  .then((res) => {
    //...
    return promise3
  })
  .then((res) => {
    //...
  })

That’s much cleaner than nesting async callbacks with the previous example.

We can make things even shorter by writing:

(async () => {
  const val1 = await promise1;
  //...
  const val2 = await promise2;
  //...
  const val3 = await promise3;
  //...
})();

As we can see, the code is much shorter and it’s exactly the same as the promise chain we have before.

The only difference is that va11 , val2 , and val3 hold the resolved values of the promises instead of res .

Conclusion

We got to know some things about JavaScript like how const aren’t always immutable, and clean up async code with async and await.

Also, arrow functions and classes should be used for regular functions and constructors respectively.

Categories
JavaScript Best Practices

Performant JavaScript Best Practices – Reduce dependencies and DOM interactions

Like any program, JavaScript programs can get slow fast if we aren’t careful with writing our code.

In this article, we’ll look at some best practices for writing fast JavaScript programs.

Reduce DOM Manipulation With Host Object and User’s Browser

DOM manipulation is slow. The more we do, the slower it’ll be. Since DOM manipulation is synchronous, each action is done one at a time, holding the rest of our program.

Therefore, we should minimize the number of DOM manipulation actions that we’re doing.

The DOM can be blocked by loading CSS and JavaScript. However, images aren’t blocking render so that they don’t hold up our page from finishing loading.

However, we still want to minimize the size of our images.

Render blocking JavaScript code can be detected with Google PageSpeed Insights, which tells us how many pieces of render-blocking JavaScript code we have.

Any inline CSS would block the rendering of the whole page. They are the styles that are scattered within our page with style attributes.

We should move them all to their own style sheets, inside the style tag, and below the body element.

CSS should be concatenated and minified to reduce the number of the stylesheet to load and their size.

We can also mark link tags as non-render blocking by using media queries. For instance, we can write the following to do that:

<link href="portrait.css" rel="stylesheet" media="orientation:portrait">

so that it only loads when the page is displayed in portrait orientation.

We should move style manipulation outside of our JavaScript and put styles inside our CSS by putting styles within their own class in a stylesheet file.

For instance, we can write the following code to add a class in our CSS file:

.highlight {  
  background-color: red;  
}

and then we can add a class with the classList object as follows:

const p = document.querySelector('p');  
p.classList.add('highlight');

We set the p element DOM object to its own constant so we can cache it and reuse it anywhere and then we call classList.add to add the hightlight class to it.

We can also remove it if we no longer want it. This way, we don’t have to do a lot of unnecessary DOM manipulation operations in our JavaScript code.

If we have scripts that no other script depends on, we can load then asynchronously so that they don’t block the loading of other scripts.

We just put async in our script tag so that we can load our script asynchronously as follows:

<script async src="script.js"></script>

Now script.js will load in the background instead of in the foreground.

We can also defer the loading of scripts by using the defer directive. However, it guarantees that the script in the order they were specified on the page.

This is a better choice if we want our scripts to load one after another without blocking the loading of other things.

Minifying scripts is also a must-do task before putting our code into production. To do that, we use module bundlers like Webpack and Parcel, which so create a project and then build them for us automatically.

Also, command-line tools for frameworks like Vue and Angular also do code minification automatically.

Minimize the Number of Dependencies Our App Uses

We should minimize the number of scripts and libraries that we use. Unused dependencies should also be removed.

For instance, if we’re using Lodash methods for array manipulation, then we can replace them with native JavaScript array methods, which are just as good.

Once we remove our dependency, we should remove them from package.json and the run npm prune to remove the dependency from our system.

Poor Event Handling

Event handling code is always slow if they’re complex. We can improve performance by reducing the depth of our call stack.

That means we call as few functions as possible. Put everything in CSS style sheets if possible if we’re manipulating styles in our event handlers.

And do everything to reduce calling functions like using the ** operator instead of calling Math.pow .

Conclusion

We should reduce the number of dependencies and loading them in an async manner if possible.

Also, we should reduce the CSS in our code and move them to their own stylesheets.

We can also add media queries so that stylesheets don’t load everywhere.

Finally, we should reduce the number of functions that are called in our code.

Categories
JavaScript Best Practices

JavaScript Best Practices – Returns, scopes, and if statements

JavaScript is a very forgiving language. It’s easy to write code that runs but has mistakes in it.

In this article, we’ll look at returning values in certain callbacks, using block-scoped variables, cleaning up the if statements, and returning clearly.


Returning Values in Callbacks Passed Into Array Methods

Many JavaScript array methods, like map, from, every, some, reduce, find, findIndex, reduceRight, filter, and sort, take a callback.

The callbacks that are passed in should return some value so that these methods can return a proper value, allowing us to do other things. For instance, the following code is probably useless and a mistake:

const arr = [1, 2, 3];
const mapped = arr.map(() => {});

Then we see that the value of mapped is [undefined, undefined, undefined]. It’s probably not what we want. Therefore, we should make sure that the callbacks that these array methods take return a value so that we don’t get something unexpected.

We shouldn’t have callbacks that don’t return any value unless we’re 100% sure that we want to do this.


Use Block-Scoped Variables

If we use var to declare variables, we should treat them as if they’re block-scoped so that we don’t cause confusion for ourselves and other developers.

For instance, we can write something like this:

foo logs true as we expected, but the confusion is that we declared var foo twice in one if block.

We should treat var like a block-scoped variable to reduce confusion, so we should instead write:

Better yet, we should use let for block-scoped variables:

let is always block-scoped so that we can’t put the variables anywhere we want like with var.


Class Method Should Reference this

Instance methods should reference this in a JavaScript class. Otherwise, there’s no point in it being an instance method. If we don’t need to reference this in a class method, then it should be a static method.

For instance, if we have:

The bar shouldn’t be an instance method since it doesn’t reference this . Instead, we should make it a static method as follows:


Limiting Linearly Independent Paths Through a Piece of Code

We shouldn’t have too many else if blocks in any if statement to reduce complexity. To make code easy to read, we should reduce the number of paths that an if statement can take.

For instance, we can write:

We should consider reducing the cases of if statements.

Photo by Rod Long on Unsplash.


Return Statements Should Specify a Value or Not Specify a Value

In JavaScript, the return statement can return undefined in multiple ways. return with nothing after it returns undefined. return with void after it and expression after void also returns undefined.

return with undefined after it also returns undefined.

We should consider returning with nothing or returning undefined or an expression explicitly to make what we’re trying to return or not return clear.

So we should either write:

const bar = () => {
  return;
}

or:

const bar = () => {
  return 1;
}

or:

const bar = () => {
  return undefined;
}

The void operator is rarely used, so most developers aren’t familiar with it. Its use is also limited. Therefore, it’s not used often.


Conclusion

Many array methods take a callback that should return a value. Instance methods like map, reduce, filter, etc. all have callbacks that should return values.

Class instance methods should reference this. If they don’t, then they should be static methods.

if statements shouldn’t be too complex. Keep the number of paths to a minimum.

Finally, return should return values or nothing rather than using the void operator with it.

Categories
JavaScript Best Practices

JavaScript Best Practice — Replacing Old with New

Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are a lot of tricky parts to JavaScript, and we can follow some best practices to improve our code.

Since ES6 was introduced, new constructs are replacing older ones for good reasons. It’s much shorter, cleaner, and easier to understand.

In this article, we’ll look at which older constructs that can be replaced with new ones, including replacing then with async and await, replacing dictionary objects with Maps, replacing apply with the spread operator, and replacing function constructors with the class syntax.

Replace Then with Async / Await

When we chain promises, we used to do it by using the then method and then returning another promise in the callback function that we pass into then.

This means that we have code that looks something like this:

Promise.resolve(1)
  .then((val) => {
    console.log(val);
    return Promise.resolve(2);
  })
  .then((val) => {
    console.log(val);
    return Promise.resolve(3);
  })
  .then((val) => {
    console.log(val);
  })

With the introduction of the async and await syntax, which is just a shorthand for calling the then method repeatedly. We can write the code above like this:

(async () => {
  const val1 = await Promise.resolve(1);
  console.log(val1);
  const val2 = await Promise.resolve(2);
  console.log(val2);
  const val3 = await Promise.resolve(3);
  console.log(val3);
})();

They both output 1, 2 and 3, but the second code snippet is so much shorter. It’s so much clearer that there’s no reason to go back to the old syntax.

Also, we can loop through promises and run them one after the other by using the for-await-of loop. We can loop through the promises we have above by rewriting the code like the following:

(async () => {
  const promises = [
    Promise.resolve(1),
    Promise.resolve(2),
    Promise.resolve(3),
  ]

  for await (let p of promises) {
    const val = await p;
    console.log(val);
  }
})();

Code like the one we have above is very handy for looping through many promises or promises that are created on the fly, which we can’t do in the earlier examples.

Replacing Dictionary Objects with Maps

ES6 also introduced the Map constructor, which lets us create hash maps or dictionaries without using JavaScript objects with string keys to do so.

Maps are also better because they have their own methods to get and manipulate the entries.

For example, instead of writing:

const dict = {
  'foo': 1,
  'bar': 2
}

We write:

const dict = new Map([
  ['foo', 1],
  ['bar', 2]
])

Then we can get an entry by using the get method as follows:

console.log(dict.get('foo'));

We can set the value of an existing entry by the key of the entry with the set method:

dict.set('foo', 2);

Also, we can check if an entry exists with the given key with the has method:

dict.has('baz');

There are also the keys and entries methods to get all the keys of the map and all the entries respectively.

For example, we can write:

console.log(dict.keys());

To get an iterator object with the keys of the Map . This means that we can loop through them with the for...of loop or convert it to an array with the spread operator.

Similarly, the entries method returns an iterator object with all the entries in the Map with each entry being an array of [key, value] .

There’s also the value method to get an iterator object with all the values in the Map .

We can also use other primitive values as keys. If we use objects, we can’t get the value back when we look them up since the lookup is done with the === operator which returns false is 2 objects that don’t have the same reference even if they have the same content.

These methods aren’t available in a regular object. Also, we might accidentally get or modify the object’s prototype’s properties instead of its own if we use the for...in loop.

Therefore, there aren’t many reasons to use a regular object as a dictionary anymore.

Photo by David Clode on Unsplash

Replace Apply with Spread

If we don’t want to change the value of this inside the function, there isn’t much reason to use the apply method in functions.

If we only want to call a function with many arguments, we can use the spread operator when passing in an array for our arguments as follows:

const arr = [1, 2, 3, 4, 5];
const add = (...args) => args.reduce((a, b) => a + b, 0);
console.log(add(...arr));

All we have to do is to use the spread operator on our array, which is the 3 dots operator in the last line, and then the array will be separated into a comma-separated list of arguments.

Replace Constructor Functions with Classes

Another great ES6 feature is the class syntax for constructor functions. It’s simply syntactic sugar that makes the constructor function looks like a class.

The advantage of it is that it makes inheritance easy.

For example, if we want to inherit from a constructor function, we have to write something like this:

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

function Employee(name, employeeCode) {
  Person.call(this, name);
  Employee.prototype.constructor = Person;
  this.employeeCode = employeeCode;
}

const employee = new Employee('Joe', 1);
console.log(employee)

This syntax looks strange coming from class-based object-oriented languages like Java.

However, the class syntax makes things look a lot familiar to developers that used other languages more than JavaScript. We can rewrite the code above to the following:

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

class Employee extends Person {
  constructor(name, employeecode) {
    super(name);
    this.employeecode = employeecode;
  }
}

const employee = new Employee('Joe', 1);
console.log(employee)

The code does the same thing as what we have above. However, it’s clearer what we’re inheriting from since we have the extends keyword to indicate what we’re inheriting from.

With the constructor function, we have to worry about the value of this in the first argument of the call method, and what we pass into the subsequent arguments of call.

With the class syntax, we don’t have to worry about this. If we forgot to make the super call like the following code:

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

class Employee extends Person {
  constructor(name, employeecode) {
    this.employeecode = employeecode;
  }
}

const employee = new Employee('Joe', 1);
console.log(employee)

We’ll get the error ‘Uncaught ReferenceError: Must call super constructor in derived class before accessing ‘this’ or returning from derived constructor.’

It’s one less chance of making a mistake.

We don’t get any error if we omit the Person.call like in the Employee constructor function since the browser doesn’t know we want Employee to inherit from Person .

In addition, when we log the prototype of employee , we get that the prototype of employee is Person as expected with the class syntax.

However, we don’t get that unless we put Employee.prototype.constructor = Person; which is easily forgotten.

Conclusion

async and await and for-await-of are new constructs that make chaining promises much cleaner. It’s much better to use them instead of using then because of it.

for-await-of also lets us loop through promises that are generated dynamically which we can’t do with then or async and await alone.

Map s are much better than plain objects for hashes and dictionaries because it has its own methods to manipulate and get the entries. Also, we may accidentally be accessing the properties of prototype of plain objects.

If we don’t want to change the value of this inside a function, we can replace the apply method for calling functions with an array of arguments with the spread operator, which does the same thing.

Finally, the class syntax for constructors is much better than the original function syntax since we can inherit from parent classes easier than setting the prototype constructor of a constructor function.