Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Async Code

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at the best practices for writing asynchronous code with Javascript.

Use Promises

Promises are very useful for writing asynchronous code in JavaScript since we can chain multiple pieces of asynchronous code together without nesting multiple asynchronous callbacks.

We can’t nest too many asynchronous callbacks deeply because it’s very hard to read and follow. Therefore, it’s very easy to create bugs and making changes will be very slow.

With promises, we can chain multiple promises together and make writing asynchronous code in a series very easy.

For instance, we can write the following code to chain multiple promises together as follows:

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

In the code above, we called Promise.resolve , which is asynchronous and returns a promise. Each Promise.resolve call is queued at the end of the event loop and runs when the JavaScript main execution thread is free of any queued tasks.

The chaining we did above is much cleaner than we would have with nested callbacks. The val just takes the value we have from the promise that’s resolved before.

As we can see, we can also take the value from the previously resolved promise and do something with it. This is something that’s hard to do with nested callbacks as we have to do 3 levels of nesting to do the same thing.

To catch errors that occurs when any promise is being executed, we can call catch with a callback as follows:

Promise.resolve(1)
  .then(val => Promise.resolve(val * 2))
  .then(val => Promise.reject('fail'))
  .catch(err => console.log(err))

In the code above, we called Promise.reject , which will cause the promise chain to fail. Promise.reject takes a reason for the failure and the reason will be available in the err object in the catch callback.

So, we’ll see 'fail' logged from the promise callback.

The promise stops running when the first promise that fails is run, so we can only have one catch call in the promise chain.

The error handling is also something that’s not available with nested async callbacks unless the code that takes the callback explicitly returns an error as a parameter in the callback.

Also, we have to do more nesting to handle errors in nested async callbacks, which means that it makes reading the code even harder and create more bugs.

To make promises even shorter, we can use the async and await syntax for chaining promises.

For instance, we can write the following code to use async and await so that we can remove the then and callbacks but do the same thing as before:

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

This is cleaner than using then and callbacks since we now have no callbacks in our code and it’s only 3 lines. Also, the readability of the code isn’t made worse by using it.

val1 , val2 , and val3 have the same values the parameter in the then callbacks, which are the resolved values.

To catch errors when a promise in the promise chain fails, we can just use try...catch as we do with synchronous code.

For instance, we can write the following code to catch promises with async and await :

(async () => {
  try {
    const val1 = await Promise.resolve(1);
    const val2 = await Promise.resolve(val1 * 2);
    await Promise.reject('fail')
  } catch (err) {
    console.log(err)
  }
})()

In the code above, we wrapped a try...catch block around our promise code so that we can catch errors with our promise chain. It stops running when the promise fails so one catch block will do everything.

Photo by Hello I’m Nik ? on Unsplash

Run Unrelated Promises in Parallel with Promise.all

We should use Promise.all to run promises that are unrelated in parallel. This way, we won’t have to wait for one promise to resolve before running another unnecessarily, thereby speeding up our code.

Promise.all takes an array of promise and returns a promise that has the array of resolved values.

For instance, we can write the following code to use Promise.all :

Promise.all([
    Promise.resolve(1),
    Promise.resolve(2),
    Promise.resolve(3),
  ])
  .then(([val1, val2, val3]) => {
    console.log(val1, val2, val3);
  })

In the code above, we have 3 promises with Promise.resolve in an array. We then use that to call Promise.all to resolve them in parallel. Then we get the resolved values in the callback and log them.

With the async and await syntax, we can write:

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

since Promise.all returns a promise.

Conclusion

When writing async code in JavaScript, we should be using promises. This way, they can be chained easily and we can also run them in parallel with Promise.all .

Removing nested increases readability by a lot and so mistakes are less likely to be made. Also, error handling is standard since we can call catch with promises to catch errors.

The async and await syntax takes chaining promises shorter. Also, we can catch errors with it with try...catch .

Categories
JavaScript Best Practices

JavaScript Best Practices — Comparison and Old Constructs

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 some best practices when writing JavaScript code, including comparisons, accessors, objects, and loops.

=== and !== are Better Than == and !=

=== and !== saves us a lot of trouble from JavaScript type coercion. The algorithm for comparison using == and != because of it.

With == we get results like:

  • [] == false
  • [] == ![]
  • 2 == "02"

all of which return true .

There’s a lot list of rules that we have to aware of listed at https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3 for == and != .

Therefore to avoid remembering all that and avoid weird results like the ones that are listed above, we should use === and !== for comparison.

So, we should avoid writing the following code:

if (x == 1) {

}

and instead, write:

if (x === 1) {

}

Other examples of good code include:

a === b
foo === true
apple !== 1
value === undefined
typeof foo === 'undefined'
'foo' !== 'bar'

Grouping Accessor Pairs in Object Literals and Classes

Grouping getters and setters for the same field together makes sense. It’s easier to read and we won’t have to search for them all over the code.

For instance, instead of writing:

const obj = {
  get a() {
    return this.val;
  },

  b: 1,

  set a(val) {
    this.val = val;
  }
}

and:

class Foo {
  get a() {
    return this.val;
  }

  b() {
    return 1;
  }

  set a(val) {
    this.val = val;
  }
}

We should instead write:

const obj = {
  get a() {
    return this.val;
  },

  set a(val) {
    this.val = val;
  },

  b: 1,
}

and:

class Foo {
  get a() {
    return this.val;
  }

  set a(val) {
    this.val = val;
  }

  b() {
    return 1;
  }
}

Checking for Own Properties in for…in Loops

for...in loop loops through an object’s own properties and also the properties up the prototype chain.

This will lead to unexpected items being looped through within the loop.

To avoid this, we should add a check to see if we’re looping through an object’s own properties so that we don’t accidentally loop through an object’s prototype’s properties.

Therefore, instead of writing:

for (const key in foo) {
  console.log(foo[key]);
}

We should write the following code instead:

for (const key in foo) {
  if (Object.prototype.hasOwnProperty.call(foo, key)) {
    console.log(foo[key]);
  }
}

The following code is also good:

for (const key in foo) {
  if ({}.hasOwnProperty.call(foo, key)) {
    console.log(foo[key]);
  }
}

In both examples, we called hasOwnPropterty with the call method to change the this value in hasOwnProperty to foo and pass in the key as the first argument to hasOwnProperty so that we can check if the key is in foo itself.

Don’t Have Too Many Classes Per File

Having too many classes per file makes it harder and read and navigate. The structure is also worse.

It’s best to limit each code file to a single responsibility. For instance, instead of writing in one file:

class Foo {}
class Bar {}

We should split them into 2 files:

foo.js :

class Foo {}

bar.js :

class Bar {}

Photo by Syed Ahmad on Unsplash

Disallow Use of Alert

JavaScript’s alert , confirm , and prompt functions are obtrusive as UI elements and should be replaced with modals, dialogs, or something else that’s less obtrusive.

It’s also often used for debugging code. Therefore, we should avoid using them unless we’re actually using them for alerting users and asking questions.

Even then, they should be used sparingly.

Stop Using arguments.caller and arguments.callee

arguments.caller and arguments.callee makes some code optimizations impossible. They’re also forbidden in strict mode.

They also don’t work in arrow functions since they don’t bind to arguments .

Therefore, we should either use arrow functions if we don’t need to bind to this . If we need to use traditional functions, then we shouldn’t reference those 2 properties in our function.

So:

function foo() {
  var callee = arguments.callee;
}

or:

function foo() {
  var caller = arguments.caller;
}

would be bad code that we want to avoid.

The alternative would be to reference the caller and callee functions directly. So arguments.callee would be foo .

Conclusion

We should use === and !== for comparison to avoid the confusing outcomes of the == and != comparison operators.

If we use accessors, then we should group the same value accessors together.

Also, maximum classes per file should be limited. The fewer the better. This limits any code file to a single responsibility.

Alerts, confirmation, and prompt pop-ups should be used as sparingly as possible.

Finally, we shouldn’t use arguments.caller and arguments.callee in traditional functions.

Categories
JavaScript Best Practices

JavaScript Best Practices — Variable Naming

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

In this article, we’ll look at best practices for naming JavaScript variables.

Computed-Value Qualifiers in Variable Names

We should name variables that hold computed data with a modifier that the end of the name.

For instance, if we have a variable that calculates a total, then we should have it with the Total at the end of the name.

If it holds the total price of a purchase, for example, we can write priceTotal .

As long as we stick with a convention for the qualifier, then no one will get confused.

Less confusion means easier to read code.

If a computed value goes that the end of the name as a rule, then we can put the modifier before it.

For instance, we can write totalPrice instead.

Common Opposites in Variable Names

If we have variables that hold the opposite data of another, then we should be precise with them.

For instance, if we have a begin variable, then we can have the end variable if it holds the opposite data of the begin variable.

Likewise, there’re also names like min and max , next and previous and so on.

Naming Loop Indexes

Loop indexes should have short variable names like i , j and k as a convention.

For instance, we may write:

for (let i = 0; i < 10; i++) {
  //...
}

However, if we have nested loops, then we may want to name the variables with a more descriptive name to make the loops more readable.

If we have a nested loop, we may write:

for (let eventIndex = 0; eventIndex < 10; eventIndex++) {
  //...
  for (let teamIndex = 0; teamIndex < 10; teamIndex++) {
    //...
  }

}

Naming Status Variables

We should name flags with a descriptive name so that we know what they hold.

For instance, we should have a flag with a name with something like isSystemReady so that we know that the flag is for holding information on whether a system is ready.

Naming Temporary Variables

Temporary variables are used to hold intermediate results of computations.

We shouldn’t treat them casually since they’re still part of the code.

It’s better that we define temporary variables with a descriptive name.

For instance, instead of writing:

const temp = (b ** 2 - 4 * a * c) ** 0.5;
let solutions = [];
solutions[0] = (-b + temp) / (2 * a);
solutions[1] = (-b - temp)

We write:

const discriminant = (b ** 2 - 4 * a * c) ** 0.5;
let solutions = [];
solutions[0] = (-b + discriminant) / (2 * a);
solutions[1] = (-b - discriminant)

Then we know that (b ** 2–4 * a * c) ** 0.5 is the discriminant of the quadratic formula.

Naming Boolean Variables

Booleans also need descriptive names.

There’re a few that pertain to boolean variables. They include done to indicate that something is done.

error to indicate whether we have an error or not.

found to indicate that something we look for is found.

success or ok indicates whether an operation has been successful.

Photo by Yoann Boyer on Unsplash

Give Boolean Variables Names that Imply true or false

The names above are good because they imply that they can only be true or false.

We may also want to put is in front of a variable to indicate that it’s a boolean.

For instance, we can define isFound instead of found ,

Use Positive Boolean Variable Names

Double negative expressions are hard on our brains. So we should have positive variable names like found instead of notFound .

Naming Constants

Constants usually are named with all upper case with underscores separating words.

For instance, we can define MAX_DONUTS to hold the maximum number of donuts someone can eat.

Why Have Conventions?

We should follow conventions to that things are easy to read and understand since people don’t have to switch context all the time.

It also prevents us from calling the same things with different names.

Conventions for names can also compensate for language weaknesses. So since JavaScript doesn’t have data type annotations, we can specify the type in the name.

Relationships among items are also indicated with consistent naming schemes.

Conclusion

We should name things with descriptive names and don’t use double negatives.

The only exceptions are loop indexes since often use letters for the names.

Constants should be all uppercase with underscores separating them.

Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Deep Checks

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at ways to find items in arrays that aren’t primitive values or items inside non-array iterable objects.

Finding Array Items That aren’t Primitive Values

To find array items that aren’t primitive values, we can’t use the indexOf method as it compares the values using the === operator when it’s doing the search.

Instead, we need to use the find or findIndex method. Both methods take a callback that takes the array entry as a parameter and returns a boolean expression with the condition of the element that we’re looking for.

The find method returns the first occurrence of an object with the given condition.

For instance, we can use the find method as follows:

const arr = [{
    user: 'foo',
    active: true
  },
  {
    user: 'bar',
    active: false
  },
];
const user = arr.find(a => a.active);

In the code above, we have the arr array with objects that have the user and active properties.

Then we used the find method to find the first occurrence of an object that has active set to true .

Then we should see that user is {user: “foo”, active: true} because that’s the first one that has active set to true .

The callback for the find method can also take the array index that’s being looped through and the array itself as the 2nd and 3rd parameters respectively.

It can also take an object that’ll be set as the value of this in the callback as an optional 2nd argument if we wish to change it.

findIndex is like find except that it returns the index of the entry of the first occurrence of something with the condition returned in the callback.

For instance, we can use it as follows:

const arr = [{
    user: 'foo',
    active: true
  },
  {
    user: 'bar',
    active: false
  },
];
const index = arr.findIndex(a => a.active);

Then index is 0 since that’s the first entry that has the active property set to true .

The arguments for findIndex is the same as find . Also, the callback parameters are also the same as find .

Finding Items That are In Non-Array Iterable Objects

We can find items that are in non-array iterable objects by converting them to arrays first and then using an array instance’s methods to find an item.

For instance, we can use this strategy to find items in a NodeList object since it’s a non-array iterable object.

We can convert a NodeList into an array by using the spread operator. For instance, we can write the following code to find an item with the class foo as follows:

const foo = [...document.body.children].find(c => c.className === 'foo')

In the code above, we want to search for the element with the class foo from the given HTML:

<p class='foo'>
  foo
</p>
<p>
  bar
</p>
<p>
  baz
</p>

We do that by getting the child elements of document.body by using the children property, which is a NodeList. A NodeList isn’t an array, but it’s an iterable object.

Therefore, we can use the spread operator to convert it to an array and then do the search with the array instance’s find method to do the search.

In the callback, we check for the className property of the element to see if it’s 'foo' .

Since we do have an element with class foo , find will return the element with such a class.

Therefore, the value of variable foo is the HTML element object with the class 'foo' .

For array-like objects that aren’t iterable. That is, objects that has the length property and numbers as keys, we can use the Array.from to convert it to an object.

For instance, we if we have:

const arrLike = {
  0: 'foo',
  1: 'bar',
  2: 'baz',
  3: 'qux',
  length: 5
};

Then we can write:

const arr = Array.from(arrLike);

to convert it to an array. Then we can use array instance methods to do the searches.

Conclusion

If we want to search for items that aren’t primitive values, then we can use the find or findIndex to search for items. They take callbacks so that we can use them to look for items in the given array.

To search for items in array-like or non-array iterable objects, we can convert them to arrays with the Array.from or the spread operator respective. This way, we can use array methods to look for items.

Categories
JavaScript Best Practices

JavaScript Best Practices for Writing More Robust Code — Checking Object Types

JavaScript is an easy to learn programming language. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust JavaScript code.

In this article, we’ll look at how to check the types for objects so that we only work on the type of objects we’re looking for.

instanceof Operator

The JavaScript instanceof operator lets us get the constructor that’s used to create the object.

The syntax for the instanceof operator is the following:

x instanceof Ctor

Where x is the object that we’re checking and Ctor is the constructor that we want to check if it’s created from it.

For instance, we can use it as follows:

function C() {}  
const c = new C();  
console.log(c instanceof C);

The code above has a new constructor function C and we created the c object by invoking the C constructor with the new operator.

Therefore, when we log c instanceof C , then we should see true because c was created from the C constructor.

Note that most objects are also an instance of Object unless we created an object specifically without a prototype.

Therefore, console.log(c instanceof Object); should also be true as we didn’t specifically remove the prototype when we created c.

The instanceof operator checks all the way up the prototype chain, so if it finds the constructor anywhere up the prototype chain, it’ll return true .

Since Object is a prototype of C and c is an instance of C , instanceof returned true .

We have to be careful when we want to check something isn’t an instance of something.

If we want to check if c isn’t an instance of C , we should write:

if (!(c instanceof C)) {  
  //...  
}

instead of:

if (!c instanceof C) {  
  //...  
}

This is because the ! operator is only applied to c rather than c instanceof C in the 2nd example. Therefore, we get false instanceof C instead of !(c instanceof C) since c is truthy and we negated it.

Constructor Name Check

An alternative to instanceof is to check the name of the constructor that created the object with the constructor.name property of an object.

For instance, if we have:

function C() {}  
const c = new C();  
console.log(c.constructor.name);

Then c.constructor.name is 'C' because we created c from the C constructor.

This only returns the name of the constructor that it’s created with so we won’t get any information about anything up the prototype chain.

Structure Checks

To check if the object is what we want to work with, we should also check if a given property and value exists.

We can do that with the object instance’s hasOwnProperty method, or we can check if the type of the property is 'undefined' or not.

It only checks if a property is an own property, which means that it’s not a property inherited from an object up the prototype chain.

For instance, we can write the following code to check that:

const obj = {};  
obj.foo = 42;  
console.log(obj.hasOwnProperty('foo'));

In the code above, we have obj.hasOwnProperty(‘foo’) . Since obj has the property foo , obj.hasOwnProperty(‘foo’) returns true .

hasOwnProperty will return true even if the value of the property is null or undefined . As long as it’s added to the object, it’ll return true .

Also, we can use the JavaScript in operator to check if a property is in an object.

The in property is used to check whether an inherited or own property is included in a given object.

For instance, if we have:

const obj = {};  
obj.foo = 42;  
console.log('foo' in obj);  
console.log('constructor' in obj);

Then both console logs show true because 'foo' is an own property of obj , while 'constructor' is a property that’s inherited from Object , which is a prototype of obj .

Like with hasOwnProperty , the in operator also returns true if the value of a property is null or undefined .

If we want to check if a property isn’t null or undefined , we can write the following:

const obj = {};  
obj.foo = 42;  
console.log(typeof obj.foo !== 'undefined' && obj.foo !== null);

In the code above, we have:

typeof obj.foo !== 'undefined' && obj.foo !== null

to check if obj.foo isn’t undefined with the typoef operator and check is obj.foo isn’t null with the !== operator.

Conclusion

We can check if an object is an instance of a constructor by using the instanceof operator or using the constructor.name property of an object.

Also, we have to check if a property is what we’re looking for so that we know that we’re working on the right object. To do that, we can use hasOwnProperty , in , or typeof operators.