Categories
JavaScript Mistakes

JavaScript Mistake — Loops, Promises, and More

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 some JavaScript mistakes, including loops and promises.

Wrong For Loop Direction

A for loop with an ending condition that’ll never be reached is probably buggy code. If we want to make an infinite loop, we should use a while loop as the convention.

For instance, if we have:

for (let i = 0; i < 20; i--) {
}

It’s probably a mistake because we specified the ending condition, but never reach it.

We probably meant:

for (let i = 0; i < 20; i++) {
}

Getters That Don’t Return Anything

If we make a getter but it doesn’t return anything, it’s most likely a mistake. There’s no reason to make a getter that returns undefined .

For instance, the following is probably incorrect:

let person = {
  get name() {}
};

Object.defineProperty(person, "gender", {
  get() {}
});

class Person {
  get name() {}
}

We have useless getters in all of the code above. If we have a getter, then we should return something in it:

let person = {
  get name() {
    return 'Jane';
  }
};

Object.defineProperty(person, "gender", {
  get() {
    return 'female';
  }
});

class Person {
  get name() {
    return 'James';
  }
}

Async Function as a Promise Executor Callback

When we define a promise from scratch, we have to pass in an executor callback function with the resolve and reject functions as parameters.

We don’t want async functions as executors because when errors are thrown, they’ll be lost and won’t cause the newly constructed promise to reject. This makes debugging and handling some errors hard.

If a promise executor function is using await , then it’s usually a sign that creates a new Promise instance is useless or the scope of the new Promise constructor can be used.

What is using await is already a promise and async functions also return a promise, so we don’t need a promise inside a promise.

For example, if we have the following code:

const fs = require('fs');

const foo = new Promise(async (resolve, reject) => {
  fs.readFile('foo.txt', (err, result)=> {
    if (err) {
      reject(err);
    } else {
      resolve(result);
    }
  });
});

const result = new Promise(async (resolve, reject) => {
  resolve(await foo);
});

Then it’s probably a mistake because we’re nesting a promise inside a promise.

What we actually want to do is:

const fs = require('fs');

const foo = new Promise(async (resolve, reject) => {
  fs.readFile('foo.txt', (err, result)=> {
    if (err) {
      reject(err);
    } else {
      resolve(result);
    }
  });
});

const result = Promise.resolve(foo);

Photo by Matthew Henry on Unsplash

No Await Inside Loops

async and await allows for parallelization. Usually, we want to run Promise.all to run unrelated promises in parallel. Running await in a loop will run each promise in sequence. This isn’t necessary for promises that don’t depend on each other.

For instance, we should write:

const bar = (results) => console.log(results);

const foo = async (arr) => {
  const promises = [];
  for (const a of arr) {
    promises.push(Promise.resolve(a));
  }
  const results = await Promise.all(promises);
  bar(results);
}

Instead of:

const bar = (results) => console.log(results);

const foo = async (arr) => {
  const results = [];
  for (const a of arr) {
    results.push(await  Promise.resolve(a));
  }
  bar(results);
}

The first example is a lot faster than the second since we’re running them in parallel instead of sequentially.

If the promises are dependent on each other, then we should use something like the 2nd example.

Don’t Compare Anything Against Negative Zero

Comparing again negative zero will return true for both +0 and -0. We probably actually want to use Object.is(x, -0) to check if something is equal to -0.

For instance, in the following code:

const x = +0;
const y = -0;
console.log(x === -0)
console.log(y === -0)

Both expressions will log true . On the other hand, if we use Object.is as follows:

const x = +0;
const y = -0;
console.log(Object.is(x, -0))
console.log(Object.is(y, -0))

Then the first log is false and the second is true , which is probably what we want.

Conclusion

There’re many ways to write code that works unintentionally with JavaScript. To prevent bugs from occurring, we should use Object.is to compare again +0 and -0, running promises inside promise executor callbacks, adding getters that don’t return anything, or creating infinite loops unintentionally.

If promises can be run in parallel, then we should take advantage of that by using Promise.all .

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 *