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
.