JavaScript - очень щадящий язык. Легко написать код, который работает, но содержит ошибки.

В этой статье мы рассмотрим некоторые ошибки JavaScript, включая циклы и обещания.

Неправильно направление петли

for цикл с конечным условием, которое никогда не будет достигнуто, вероятно, является ошибочным кодом. Если мы хотим создать бесконечный цикл, мы должны использовать цикл while в качестве соглашения.

Например, если у нас есть:

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

Вероятно, это ошибка, потому что мы указали конечное условие, но так и не достигли его.

Вероятно, мы имели в виду:

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

Получатели, которые ничего не возвращают

Если мы делаем геттер, но он ничего не возвращает, скорее всего, это ошибка. Нет причин делать геттер, возвращающий undefined.

Например, вероятно, неверно следующее:

let person = {
  get name() {}
};
Object.defineProperty(person, "gender", {
  get() {}
});
class Person {
  get name() {}
}

У нас есть бесполезные геттеры во всем приведенном выше коде. Если у нас есть геттер, то мы должны что-то в нем вернуть:

let person = {
  get name() {
    return 'Jane';
  }
};
Object.defineProperty(person, "gender", {
  get() {
    return 'female';
  }
});
class Person {
  get name() {
    return 'James';
  }
}

Асинхронная функция как обратный вызов исполнителя обещаний

Когда мы определяем обещание с нуля, мы должны передать функцию обратного вызова исполнителя с функциями resolve и reject в качестве параметров.

Мы не хотим, чтобы async выполняли функции исполнителей, потому что при возникновении ошибок они будут потеряны и не приведут к отклонению вновь созданного обещания. Это затрудняет отладку и обработку некоторых ошибок.

Если функция исполнителя обещания использует await, то обычно это означает, что создание нового Promise экземпляра бесполезно или можно использовать область действия конструктора new Promise.

То, что использует await, уже является обещанием, и async функции также возвращают обещание, поэтому нам не нужно обещание внутри обещания.

Например, если у нас есть следующий код:

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);
});

Тогда это, вероятно, ошибка, потому что мы вкладываем обещание в обещание.

На самом деле мы хотим:

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);

Нет ожидания внутри циклов

async и await позволяют распараллеливать. Обычно мы хотим запустить Promise.all, чтобы параллельно выполнять несвязанные обещания. Выполнение await в цикле приведет к последовательному выполнению каждого обещания. Это не обязательно для обещаний, которые не зависят друг от друга.

Например, мы должны написать:

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);
}

Вместо того:

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);
}

Первый пример намного быстрее, чем второй, поскольку мы запускаем их параллельно, а не последовательно.

Если обещания зависят друг от друга, мы должны использовать что-то вроде 2-го примера.

Не сравнивайте ничего с отрицательным нулем

Повторное сравнение отрицательного нуля вернет true как для +0, так и для -0. Вероятно, мы действительно хотим использовать Object.is(x, -0), чтобы проверить, равно ли что-то -0.

Например, в следующем коде:

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

Оба выражения будут записывать true. С другой стороны, если мы используем Object.is следующим образом:

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

Тогда первый журнал - это false, а второй - true, что, вероятно, нам и нужно.

Заключение

Есть много способов написать код, который непреднамеренно работает с JavaScript. Чтобы предотвратить появление ошибок, мы должны использовать Object.is для повторного сравнения +0 и -0, выполнения обещаний внутри обратных вызовов исполнителя обещаний, добавления геттеров, которые ничего не возвращают, или непреднамеренного создания бесконечных циклов.

Если обещания могут выполняться параллельно, мы должны воспользоваться этим, используя Promise.all.