Categories
JavaScript Mistakes

JavaScript Mistakes — Async, Type Checking, and Values

Spread the love

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 how to avoid race conditions when running multiple promises and more.

Assignments that can Lead to Race Conditions Because of yield and await

When using promises, it’s easy to create race conditions when we aren’t combining multiple promises properly.

For instance, if we have:

let total = 0;

const getNum = async (num) => {
  return num;
}

const add = async (num) => {
  total += await getNum(num);
}

Promise.all([add(1), add(2)]).then(() => {
  console.log(total);
});

Then we’re calling the add async function, which gets the result from the getNum promise and add it to the total .

Then we call Promise.all , which runs the code in parallel. In that time, the first value assigned to total will be overwritten because total is still set to the initial value in each promise before they were resolved.

Therefore, we’ll see 2 instead of 3 as we expect.

We can fix this mistake by assigning the resolved value in add to a variable before adding them to the total as follows:

const add = async (num) => {
  const res = await getNum(num);
  total += res;
}

Or we can just wait for both to resolve and then add the results together:

const getNum = async (num) => {
  return num;
}

const add = async (num) => {
  return await getNum(num);
}

Promise.all([add(1), add(2)]).then((nums) => {
  const total = nums.reduce((a, b) => a + b, 0);
  console.log(total);
});

Not Using isNaN to Check for NaN

NaN is a special value in JavaScript. It doesn’t equal itself when compared with == or === . For instance, the following:

NaN === NaN
NaN == NaN

returns false and:

NaN !== NaN
NaN != NaN

returns true .

NaN check also doesn’t work with indexOf and lastIndexOf :

const arr = [NaN];
console.log(arr.indexOf(NaN));
console.log(arr.`lastIndexOf`(NaN));

We’ll get -1 returned from the console log in the code above.

Therefore, we should check for NaN with the isNaN function as follows:

const foo = NaN;
if (isNaN(foo)){
  console.log('is NaN');
}

We can also use the Object.is method to check for NaN as follows:

const foo = NaN;
if (Object.is(foo, NaN)) {
  console.log('is NaN');
}

To check if NaN is in an array, we can write our own function to do it:

const arr = [NaN];
console.log(arr.some(a => isNaN(a)));

Compare typeof Expressions Against Invalid Strings

It’s easy to make typos when writing strings. Therefore, we should check if the types that we check with typeof are correct.

For instance, the following code has typos in the string that’ll cause typeof to not work as we expect them to:

typeof foo === "strnig"
typeof foo == "undefimed"
typeof bar === "nunber"
typeof bar === "fucntion"

We should make sure that the strings that we compare against are spelled correctly as follows:

typeof foo === "string"
typeof foo == "undefined"
typeof bar === "number"
typeof bar === "function"

Getters and Setters should be Together in Object and Classes

A setter isn’t very useful without its corresponding getter. For instance, if we have:

const obj = {
  set foo(val) {
    this._foo = val
  }
}

Then there’s no way to get the value of foo , and we’ll get undefined when we reference obj.foo even after we set the value.

Therefore, we should add a corresponding getter to the object to return a value. For instance, we should instead write:

const obj = {
  get foo() {
    return this._foo;
  },
  set foo(val) {
    this._foo = val
  }
}

To define a new property with defineProperty with getters and setters, we can write the following:

const obj = {};
Object.defineProperty(obj, 'foo', {
  get() {
    return this._foo;
  },
  set(val) {
    this._foo = val
  }
});

Then if we assign a value to foo , we can get the value of the foo property. Likewise, we can do the same for classes as follows:

class Obj {
  get foo() {
    return this._foo;
  }

  set foo(val) {
    this._foo = val;
  }
}

const obj = new Obj();
obj.foo = 1;

This works the same as the object example, except that we’ve to instantiate it with new first.

Conclusion

We shouldn’t do assignment with operations like += with await or yield as the right operand as the value will be overwritten instead of combined with the existing value.

Also, we should use isNaN to check for NaN instead of checking with the === or == operator.

When using typeof , we should check for typos in the strings that we check for the types against.

Setters without getters also isn’t very useful since we can’t get the value that we set.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *