Какие вы можете привести антипаттерны (или примеры плохого стиля) программирования для node.js?nodejs-61

Node.js имеет свою специфику, и некоторые подходы, которые могут работать в других средах, здесь становятся антипаттернами. Рассмотрим наиболее распространенные.

1. Callback Hell

// Плохой пример
fs.readFile('file1.txt', 'utf8', (err, data1) => {
  if (err) throw err;
  fs.readFile('file2.txt', 'utf8', (err, data2) => {
    if (err) throw err;
    fs.writeFile('result.txt', data1 + data2, (err) => {
      if (err) throw err;
      console.log('Done!');
    });
  });
});

Решение: Использовать Promises/async-await или библиотеки типа async.js

// Хороший пример
async function processFiles() {
  try {
    const [data1, data2] = await Promise.all([
      fs.promises.readFile('file1.txt', 'utf8'),
      fs.promises.readFile('file2.txt', 'utf8')
    ]);
    await fs.promises.writeFile('result.txt', data1 + data2);
    console.log('Done!');
  } catch (err) {
    console.error(err);
  }
}

2. Блокировка event loop

// Плохой пример - синхронные операции в обработчике запроса
app.get('/data', (req, res) => {
  const data = fs.readFileSync('huge-file.json'); // Блокирует event loop
  res.send(data);
});

Решение: Всегда использовать асинхронные API

3. Неправильная обработка ошибок

// Плохой пример 1 - игнорирование ошибок
fs.readFile('file.txt', (err, data) => {
  console.log(data); // Что если err?
});

// Плохой пример 2 - неправильное использование rejections
new Promise((resolve, reject) => {
  someAsyncOperation((err) => {
    if (err) throw err; // Не попадет в catch!
  });
});

Решение: Всегда обрабатывать ошибки явно

4. Глобальное состояние

// Плохой пример - использование глобальных переменных
let cache = {};

app.get('/data', (req, res) => {
  if (!cache.data) {
    cache.data = fetchData(); // Проблемы при кластеризации
  }
  res.send(cache.data);
});

Решение: Использовать специализированные решения (Redis, Memcached)

5. Неправильная работа с потоками

// Плохой пример - отсутствие обработки backpressure
readable.pipe(writable); // Без обработки переполнения

Решение: Всегда учитывать обратное давление

readable.on('data', (chunk) => {
  if (!writable.write(chunk)) {
    readable.pause();
    writable.once('drain', () => readable.resume());
  }
});

6. "Толстые" middleware

// Плохой пример - middleware с множеством ответственностей
app.use((req, res, next) => {
  authenticate(req);
  logRequest(req);
  validateParams(req);
  checkPermissions(req);
  transformData(req);
  next(); // Слишком сложно для тестирования
});

Решение: Разделять middleware на специализированные

7. Неэффективное использование памяти

// Плохой пример - загрузка всего файла в память
app.post('/upload', (req, res) => {
  let data = '';
  req.on('data', chunk => data += chunk); // Проблема с большими файлами
  req.on('end', () => processData(data));
});

Решение: Использовать потоки

app.post('/upload', (req, res) => {
  const transform = new TransformStream({ /* ... */ });
  req.pipe(transform).pipe(res);
});

8. Игнорирование окружения

// Плохой пример - жесткие зависимости от окружения
if (process.env.NODE_ENV === 'production') {
  db.connect('prod:password@localhost'); // Креды в коде
}

Решение: Использовать config-файлы и переменные окружения правильно

9. "Волшебные" числа и строки

// Плохой пример
setTimeout(() => {}, 86400000); // Что это за число?
if (status === 4) { ... } // Что означает 4?

Решение: Использовать константы и enums

10. Неправильное использование require

// Плохой пример - require внутри функций
function getConfig() {
  return require('./config.json'); // Кэшируется только при первом вызове
}

// Плохой пример - циклические зависимости
// a.js
require('./b');
// b.js
require('./a');

Решение: require только на верхнем уровне модуля

Резюмируем:

Node.js-разработка требует особого внимания к асинхронности, управлению памятью и потоками данных. Избегая этих антипаттернов, вы создадите более надежные, производительные и поддерживаемые приложения. Ключевые принципы: асинхронность везде где возможно, правильная обработка ошибок, избегание глобального состояния и грамотная работа с ресурсами.