10 главных ошибок, которые совершают Node-разработчики

Даже опытные Node-разработчики зачастую совершают банальные ошибки. Рассмотрим десятку распространенных и разберемся, как их избежать.

10 самых популярных ошибок Node-разработчиков

Популярность платформы Node.js в последние годы растет невероятными темпами. Ее используют для разработки такие гиганты, как Walmart и PayPal. Модулей в npm в сотни раз больше, чем в других пакетных менеджерах, и увеличивается каждый день. Но все же, несмотря на огромное количество поклонников, Node.js – не самый простой инструмент. Чтобы разобраться в его тонкостях и понять философию, нужно потрудиться.

Код всех примеров в статье можно найти на Github.

Пренебрежение инструментами разработчика

В отличие от PHP или Ruby, приложения на Node необходимо перезапускать после изменения исходного кода. А во время разработки нужно постоянно обновлять браузер, чтобы увидеть результаты вносимых правок. Конечно, все это можно делать вручную, но зачем? Среди тысяч npm-пакетов найдутся те, что могут взять этот труд на себя.

Автоматический рестарт

Что вы обычно делаете после сохранения изменений в кодовом редакторе? Эта комбинация уже закрепилась в разработчиках на уровне инстинктов:

  • остановить приложение – CTRL+C;
  • вернуться к предыдущей команде в терминале – UP;
  • запустить приложение снова – ENTER.

Хватит повторять это раз за разом! Сделайте процесс разработки проще с помощью готовых инструментов, например:

Эти молчаливые помощники наблюдают за файлами проекта и при их изменении автоматически перезапускают сервер.

Возьмем для примера nodemon. Сначала пакет нужно установить глобально:

npm i nodemon -g

А теперь просто замените команду запуска node на nodemon:

# node server.js

$ nodemon server.js
14 Nov 21:23:23 - [nodemon] v1.2.1
14 Nov 21:23:23 - [nodemon] to restart at any time, enter `rs`
14 Nov 21:23:23 - [nodemon] watching: *.*
14 Nov 21:23:23 - [nodemon] starting `node server.js`
14 Nov 21:24:14 - [nodemon] restarting due to changes...
14 Nov 21:24:14 - [nodemon] starting `node server.js`

Среди множества полезных опций пакетов nodemon и node-supervisor, разумеется, есть и возможность игнорирования определенных директорий или отдельных файлов.

Живая перезагрузка

Теперь поговорим о процессе разработки веб-приложений. Согласитесь, что самая скучная его часть – обновлять страничку в браузере. Эту операцию тоже можно автоматизировать с помощью npm-пакета livereload или ему подобных.

Они работают точно так же, как nodemon и node-supervisor: следят за файлами проекта и реагируют на их изменения. Только вместо перезапуска сервера происходит рефреш веб-страницы с помощью скрипта или специального браузерного плагина.

Мы не будем просто смотреть, как работает livereload, а сразу сделаем приложение на Node. Оно будет следить за файлами в папке, рассылать сообщения подключенным клиентам с помощью технологии server-sent events и обновлять страницу.

Сначала установим все необходимые npm-зависимости:

  • express станет основой для приложения;
  • watch поможет следить за изменениями в файлах;
  • sendevent обеспечит server-sent event (вместо SSE можно использовать веб-сокеты);
  • uglify-js будет минифицировать JavaScript файлы;
  • ejs поможет с представлениями.

Напишем простой Express-сервер, который будет рендерить главную страницу из представления home.

var express = require('express');
var app = express();
var ejs = require('ejs');
var path = require('path');

var PORT = process.env.PORT || 1337;

// установка движка представлений
app.engine('html', ejs.renderFile);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');

// обработка запроса к главной странице
app.get('/', function(req, res) {
  res.render('home');
});

app.listen(PORT);
console.log('server started on port %s', PORT);

Раз уж мы работаем с Express, то инструмент для обновления браузера будем создавать как промежуточное ПО. Он определит конечную точку для SSE, а также создаст хелпер подключения клиентского скрипта для представления. В качестве аргументов обработчик принимает переменную app и папку, файлы в которой нужно отслеживать. Мы уже можем добавить следующие строчки в server.js перед установкой представлений:

var reloadify = require('./lib/reloadify');
reloadify(app, __dirname + '/views');

На предмет изменений будут проверяться файлы в папке /views. Теперь переходим к самому обработчику:

var sendevent = require('sendevent');
var watch = require('watch');
var uglify = require('uglify-js');
var fs = require('fs');
var ENV = process.env.NODE_ENV || 'development';

// создание и минификация статического js-файла
var polyfill = fs.readFileSync(__dirname + '/assets/eventsource-polyfill.js', 'utf8');
var clientScript = fs.readFileSync(__dirname + '/assets/client-script.js', 'utf8');
var script = uglify.minify(polyfill + clientScript, { fromString: true }).code;

function reloadify(app, dir) {
  if (ENV !== 'development') {
    app.locals.watchScript = '';
    return;
  }

  // создание промежуточного ПО 
  // для обработки запросов к `/eventstream`
  var events = sendevent('/eventstream');

  app.use(events);

  watch.watchTree(dir, function (f, curr, prev) {
    events.broadcast({ msg: 'reload' });
  });

  // переменная для подключения скрипта в представление
  app.locals.watchScript = '<script>' + script + '</script>';
}

module.exports = reloadify;

Если переменная NODE_ENV не равна «development», обработчик просто ничего не делает. Это значит, что для продакшена его нельзя использовать.

Клиентский JavaScript-файл очень простой. Он слушает SSE-сообщения и при необходимости перезагружает страницу:

(function() {

  function subscribe(url, callback) {
    var source = new window.EventSource(url);

    source.onmessage = function(e) {
      callback(e.data);
    };

    source.onerror = function(e) {
      if (source.readyState == window.EventSource.CLOSED) return;
      console.log('sse error', e);
    };

    return source.close.bind(source);
  };

  subscribe('/eventstream', function(data) {
    if (data && /reload/.test(data)) {
      window.location.reload();
    }
  });
}());

Файл eventsource-polyfill.js это специальный полифилл для SSE.

И последнее, что осталось сделать, – подключить сгенерированный скрипт на страницу /views/home.html с помощью хелпера:

...
<%- watchScript %>
...

Теперь каждый раз при изменении файла home.html браузер будет автоматически перезагружать домашнюю страницу (http://localhost:1337/).

Блокировка цикла событий Node

Так как Node.js работает в одном потоке, блокировка цикла обработки событий означает блокировку вообще всего. Если у вас есть веб-сервер с тысячами подключенных клиентов и вы заблокировали event loop, им всем придется подождать, пока вы не одумаетесь.

Вот несколько действий, которые могут стать причиной простоя:

Спорим, что вы даже не подозревали об этом! Вы можете делать все это неосознанно, ведь мы не так уж часто парсим 15 Мб данных за один раз. Но если злоумышленник застанет вас врасплох, то сервер может пострадать от DDOS-атаки.

К счастью, цикл событий можно контролировать на предмет задержек и других аномалий. Для этого существуют проприетарные решения, например, StrongOps, и ряд модулей с открытым исходным кодом, например, blocked.

Идея заключается в том, чтобы точно отслеживать время между интервалами. Чтобы рассчитать задержку, берется момент A и момент B и вычисляется разница между ними.

Рассмотрим следующий пример, который:

  • очень точно определяет разницу между текущим временем и моментом, переданным в качестве параметра;
  • через регулярные промежутки определяет задержку цикла событий;
  • отображает ее зеленым или красным цветом.

Для наглядности каждые 300 миллисекунд мы будем серьезно нагружать цикл.

var getHrDiffTime = function(time) {
  // ts = [seconds, nanoseconds]
  var ts = process.hrtime(time);
  // конвертация секунд и наносекунд в милисекунды
  return (ts[0] * 1000) + (ts[1] / 1000000);
};

var outputDelay = function(interval, maxDelay) {
  maxDelay = maxDelay || 100;

  var before = process.hrtime();

  setTimeout(function() {
    var delay = getHrDiffTime(before) - interval;

    if (delay < maxDelay) {
      console.log('delay is %s', chalk.green(delay));
    } else {
      console.log('delay is %s', chalk.red(delay));
    }

    outputDelay(interval, maxDelay);
  }, interval);
};

outputDelay(300);

// затратная операция каждые 2 секунды
setInterval(function compute() {
  var sum = 0;

  for (var i = 0; i <= 999999999; i++) {
    sum += i * 2 - (i + 1);
  }
}, 2000);

Не забудьте установить chalk перед запуском.

Вот что наш пример выводит в терминал:

10 самых популярных ошибок Node-разработчиков

Модули с открытым исходным кодом работают точно так же, поэтому вы можете смело их использовать:

Если связать этот метод с профилированием, можно точно определить, какая именно часть кода вызвала задержку.

Многократный вызов коллбэка

Сколько раз вы сохраняли файл и перезагружали Node-приложение только для того, чтобы увидеть его моментальный сбой? Скорее всего, вы дважды выполнили обратный вызов, то есть не остановили функцию после первого раза.

Чтобы воспроизвести ситуацию, создадим простой прокси-сервер с некоторой базовой проверкой. Установите зависимость request, запустите программу и откройте, например, http://localhost:1337/?url=http://www.google.com/.

var request = require('request');
var http = require('http');
var url = require('url');
var PORT = process.env.PORT || 1337;

var expression = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi;
var isUrl = new RegExp(expression);

var respond = function(err, params) {
  var res = params.res;
  var body = params.body;
  var proxyUrl = params.proxyUrl;

  res.setHeader('Content-type', 'text/html; charset=utf-8');

  if (err) {
    console.error(err);
    res.end('An error occured. Please make sure the domain exists.');
  } else {
    res.end(body);
  }
};

http.createServer(function(req, res) {
  var queryParams = url.parse(req.url, true).query;
  var proxyUrl = queryParams.url;

  if (!proxyUrl || (!isUrl.test(proxyUrl))) {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write("Please provide a correct URL param. For ex: ");
    res.end("<a href='http://localhost:1337/?url=http://www.google.com/'>http://localhost:1337/?url=http://www.google.com/</a>");
  } else {
    // ------------------------
    // Само проксирование здесь
    // ------------------------
  }
}).listen(PORT);

Этот исходный код содержит почти все, кроме самого проксирования. Оно вынесено в отдельный фрагмент, чтобы вы внимательно на него посмотрели:

request(proxyUrl, function(err, r, body) {
  if (err) {
    respond(err, {
    res: res,
    proxyUrl: proxyUrl
    });
  }

  respond(null, {
    res: res,
    body: body,
    proxyUrl: proxyUrl
  });
});

В обратном вызове обрабатывается ошибка, но после этого поток выполнения не останавливается. Таким образом, если программа получит неправильный адрес, функция respond будет вызвана дважды, и в терминале появится следующее сообщение:

Error: Can't set headers after they are sent.
      at ServerResponse.OutgoingMessage.setHeader (http.js:691:11)
      at respond (/Users/alexandruvladutu/www/airpair-2/3-multi-callback/proxy-server.js:18:7)

This can be avoided either by using the `return` statement or by wrapping the 'success' callback in the `else` statement:

Решить проблему можно так:

request(.., function(..params) {
  if (err) {
    return respond(err, ..);
  }

  respond(..);
});

или так:

request(.., function(..params) {
  if (err) {
    respond(err, ..);
  } else {
    respond(..);
  }
});

Ад коллбэков

Каждый, кто хочет раскритиковать Node.js, вспоминает об «аде коллбэков». Многие уверены, что от вложенных обратных вызовов никуда не деться, но это совсем не так. Есть несколько решений для поддержки чистоты кода, например:

Для примера создадим приложение и переработаем его для использования модуля async. Это будет простой анализатор ресурсов интерфейса, который делает следующее:

  • Проверяет, сколько скриптов / стилей / изображений подключается в HTML-коде;
  • Выводит в консоль их общее количество;
  • Проверяет размер каждого ресурса;
  • Выводит в терминал сумму этих размеров.

Помимо async, мы будем использовать следующие npm-модули:

  • request для получения данных страницы (тело, заголовки и т. д.);
  • cheerio как бэкенд-аналог jQuery;
  • once, чтобы убедиться, что обратный вызов выполняется только один раз.
var URL = process.env.URL;
var assert = require('assert');
var url = require('url');
var request = require('request');
var cheerio = require('cheerio');
var once = require('once');
var isUrl = new RegExp(/[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi);

assert(isUrl.test(URL), 'must provide a correct URL env variable');

request({ url: URL, gzip: true }, function(err, res, body) {
  if (err) { throw err; }

  if (res.statusCode !== 200) {
    return console.error('Bad server response', res.statusCode);
  }

  var $ = cheerio.load(body);
  var resources = [];

  $('script').each(function(index, el) {
    var src = $(this).attr('src');
    if (src) { resources.push(src); }
  });

  // .....
  // аналогичный код для файлов стилей и изображений
  // полная версия на github

  var counter = resources.length;
  var next = once(function(err, result) {
    if (err) { throw err; }

    var size = (result.size / 1024 / 1024).toFixed(2);

    console.log('There are ~ %s resources with a size of %s Mb.', result.length, size);
  });

  var totalSize = 0;

  resources.forEach(function(relative) {
    var resourceUrl = url.resolve(URL, relative);

    request({ url: resourceUrl, gzip: true }, function(err, res, body) {
      if (err) { return next(err); }

      if (res.statusCode !== 200) {
        return next(new Error(resourceUrl + ' responded with a bad code ' + res.statusCode));
      }

      if (res.headers['content-length']) {
        totalSize += parseInt(res.headers['content-length'], 10);
      } else {
        totalSize += Buffer.byteLength(body, 'utf8');
      }

      if (!--counter) {
        next(null, {
          length: resources.length,
          size: totalSize
        });
      }
    });
  });
});

Выглядит не так уж ужасно, но вложенность может быть еще глубже. Распознать пирамиду коллбэков очень просто. Достаточно посмотреть на отступы и скобки в нижней части кода:

      if (!--counter) {
        next(null, {
          length: resources.length,
          size: totalSize
        });
      }
    });
  });
});

Команда для запуска приложения:

$ URL=https://bbc.co.uk/ node before.js
# Sample output:
# There are ~ 24 resources with a size of 0.09 Mb.

После небольшого рефакторинга с использованием async код преображается:

var async = require('async');

var rootHtml = '';
var resources = [];
var totalSize = 0;

var handleBadResponse = function(err, url, statusCode, cb) {
  if (!err && (statusCode !== 200)) {
    err = new Error(URL + ' responded with a bad code ' + res.statusCode);
  }

  if (err) {
    cb(err);
    return true;
  }

  return false;
};

async.series([  
  function getRootHtml(cb) {
    request({ url: URL, gzip: true }, function(err, res, body) {
      if (handleBadResponse(err, URL, res.statusCode, cb)) { return; }

      rootHtml = body;

      cb();
    });
  },
  function aggregateResources(cb) {
    var $ = cheerio.load(rootHtml);

    $('script').each(function(index, el) {
      var src = $(this).attr('src');
      if (src) { resources.push(src); }
    });

    // аналогичный код для файлов стилей и изображений
    // полная версия на github

    setImmediate(cb);
  },
  function calculateSize(cb) {
    async.each(resources, function(relativeUrl, next) {
      var resourceUrl = url.resolve(URL, relativeUrl);

      request({ url: resourceUrl, gzip: true }, function(err, res, body) {
        if (handleBadResponse(err, resourceUrl, res.statusCode, cb)) { return; }

        if (res.headers['content-length']) {
          totalSize += parseInt(res.headers['content-length'], 10);
        } else {
          totalSize += Buffer.byteLength(body, 'utf8');
        }

        next();
      });
    }, cb);
  }
], function(err) {
  if (err) { throw err; }

  var size = (totalSize / 1024 / 1024).toFixed(2);
  console.log('There are ~ %s resources with a size of %s Mb.', resources.length, size);
});

Создание больших монолитных приложений

Разработчики часто приходят в Node, сохранив образ мышления других языков. Они делают некоторые вещи так, как привыкли, а не так, как следовало бы. Например, размещают весь проект в одном огромном файле, не разбивая его на модули и npm-пакеты. Такой код сложно читать и невозможно тестировать.

Но не переживайте! Сделать приложение более понятным и модульным несложно. Это также поможет справиться с проблемой «пирамиды коллбэков», если она вас беспокоит.

Извлечем валидатор URL, а также все обработчики в отдельные модули. Тогда основной файл будет выглядеть так:

  // ...
  var handleBadResponse = require('./lib/bad-response-handler');
  var isValidUrl = require('./lib/url-validator');
  var extractResources = require('./lib/resource-extractor');
  var request = require('./lib/requester');

  // ...
  async.series([
    function getRootHtml(cb) {
      request(URL, function(err, data) {
        if (err) { return cb(err); }
        rootHtml = data.body;
        cb(null, 123);
      });
    },
    function aggregateResources(cb) {
      resources = extractResources(rootHtml);
      setImmediate(cb);
    },
    function calculateSize(cb) {
      async.each(resources, function(relativeUrl, next) {
        var resourceUrl = url.resolve(URL, relativeUrl);
        request(resourceUrl, function(err, data) {
          if (err) { return next(err); }
          if (data.res.headers['content-length']) {
            totalSize += parseInt(data.res.headers['content-length'], 10);
          } else {
            totalSize += Buffer.byteLength(data.body, 'utf8');
          }
          next();
        });
      }, cb);
    }
  ], function(err) {
    if (err) { throw err; }
    var size = (totalSize / 1024 / 1024).toFixed(2);
    console.log('\nThere are ~ %s resources with a size of %s Mb.', resources.length, size);
  }
);

А это обработчик запроса:

var handleBadResponse = require('./bad-response-handler');
var request = require('request');

module.exports = function getSiteData(url, callback) {
  request({
    url: url,
    gzip: true,
    // ...
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36'
    }
  }, function(err, res, body) {
    if (handleBadResponse(err, url, res && res.statusCode, callback)) { return; }

    callback(null, {
      body: body,
      res: res
    });
  });
};

Полный код примера вы можете найти на Github.

Все стало гораздо проще, легче для чтения и удобнее для тестов.

Node поощряет разработчиков писать небольшие модули и публиковать их в npm. Там можно найти самые разные полезные инструменты, например, для генерации случайного числа в заданном интервале. Вы должны стремиться к модульности и максимально упрощать свои Node-приложения.

Недостаточное логирование

Во многих руководствах по Node.js есть небольшие демонстрационные примеры, использующие для логирования команду console.log. В результате у разработчиков складывается впечатление, что именно так и нужно делать.

Но есть вещи, которые гораздо круче console.log, потому что они:

  • не требуют использования util.inspect для сложных больших объектов;
  • имеют встроенные сериализаторы для объектов ошибок, запросов и ответов;
  • поддерживают несколько источников для управления логами;
  • могут вести журнал на разных уровнях (отладка, информация, обычные и фатальные ошибки);
  • автоматически включают в лог имя хоста, id процесса и имя приложения;
  • поддерживают ротацию логов.

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

Давайте разберем пример:

var http = require('http');
var bunyan = require('bunyan');

var log = bunyan.createLogger({
  name: 'myserver',
  serializers: {
    req: bunyan.stdSerializers.req,
    res: bunyan.stdSerializers.res
  }
});

var server = http.createServer(function (req, res) {
  log.info({ req: req }, 'start request');  // <-- тестовая команда
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello World\n');
  log.info({ res: res }, 'done response');  // <-- тестовая команда
});

server.listen(1337, '127.0.0.1', function() {
  log.info('server listening');

  var options = {
    port: 1337,
    hostname: '127.0.0.1',
    path: '/path?q=1#anchor',
    headers: {
      'X-Hi': 'Mom'
    }
  };

  var req = http.request(options, function(res) {
    res.resume();
    res.on('end', function() {
      process.exit();
    })
  });

  req.write('hi from the client');
  req.end();
});

Если запустить его из командной строки, вы увидите что-то вроде этого:

$ node server.js
  {"name":"myserver","hostname":"MBP.local","pid":14304,"level":30,"msg":"server listening","time":"2014-11-16T11:30:13.263Z","v":0}
  {"name":"myserver","hostname":"MBP.local","pid":14304,"level":30,"req":{"method":"GET","url":"/path?q=1#anchor","headers":{"x-hi":"Mom","host":"127.0.0.1:1337","connection":"keep-alive"},"remoteAddress":"127.0.0.1","remotePort":61580},"msg":"start request","time":"2014-11-16T11:30:13.271Z","v":0}
  {"name":"myserver","hostname":"MBP.local","pid":14304,"level":30,"res":{"statusCode":200,"header":"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nDate: Sun, 16 Nov 2014 11:30:13 GMT\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n"},"msg":"done response","time":"2014-11-16T11:30:13.273Z","v":0}

В разработке лучше использовать CLI-инструмент:

10 самых популярных ошибок Node-разработчиков

Как видно, bunyan дает множество полезной информации о текущем процессе, что жизненно важно для работающего приложения. Есть еще полезная возможность подключать логи к потоку (или нескольким потокам).

Пренебрежение тестами

Приложение не может считаться «готовым», если оно не протестировано. Отсутствию тестов нет никакого оправдания, учитывая, сколько удобных инструментов для этого существует:

  • фреймворки mocha, jasmine, tape и десятки других;
  • пакеты для тестовых утверждений chai, should.js;
  • модули для создания заглушек и mock-объектов наподобие sinon;
  • инструменты покрытия кода istanbul, blanket.

По соглашению npm-модули должны иметь команду для запуска тестов, которая указывается в файле package.json.

{
  "name": "express",
  ...
  "scripts": {
    "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
    ...
  }
}

Благодаря этому тесты можно запускать с помощью команды npm test, не вдаваясь в подробности реализации тестовых методов.

Прежде чем закоммитить любое изменение проекта, необходимо запустить тесты. К счастью, это так же просто, как набрать команду npm i pre-commit --save-dev.

Также вы можете установить определенный уровень покрытия кода и запретить все коммиты, несоответствующие ему. Модуль pre-commit просто автоматически запускает npm test перед фиксацией изменений.

Если вы не знаете с чего начать, чтобы освоить тестирование, обратитесь к онлайн-руководствам или изучите популярные Node-проекты на Github, например:

Пренебрежение линтингом кода

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

ESLint и ему подобные инструменты решают огромное количество проблем:

  • Устраняют потенциальные ошибки, например, присваивание в условных выражения, использование debugger;
  • Принудительно вводят хорошие практики кода, например, запрещают объявление переменной более одного раза или применение устаревшей конструкции arguments.callee;
  • Ищут возможные проблемы безопасности, например, eval() или небезопасные регулярные выражения;
  • Обнаруживают фрагменты кода с низкой производительностью;
  • Обеспечивают последовательное применение руководства по стилю.

Для более полного понимания ознакомьтесь с документацией ESLint. Вы также можете подробнее узнать о конфигурации, чтобы максимально точно настроить инструмент для вашего проекта.

Существуют и другие аналогичные пакеты для линтинга кода, например, JSLint или JSHint.

Если вы хотите самостоятельно разобрать АСД (абстрактное синтаксическое дерево) и создать собственный анализатор, рассмотрите Esprima или Acorn.

Отсутствие мониторинга или профилирования кода

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

Есть ряд проприетарных сервисов, которые могут позаботиться об этих вещах вместо вас: New Relic, StrongLoop или Concurix, AppDynamics.

Но вы можете сделать это самостоятельно с помощью модулей с открытым исходным кодом (look) или комбинации нескольких пакетов. Независимо от выбора, убедитесь, что вы всегда знаете статус своего приложения.

Отладка с помощью console.log

Когда что-то идет не так, легко вставить console.log и отладить программу. После этого вы просто удаляете строку и продолжаете писать.

Но следующий разработчик, которому придется поддерживать ваш код, может наткнуться на эту же самую проблему. Вот для чего нужны модули вроде debug. Вместо того чтобы вставлять и удалять console.log, вы можете заменить его на функцию debug и оставить ее в коде.

Когда следующий программист попытается понять этот фрагмент, он может просто запустить приложение, установив переменную окружения DEBUG.

Этот крошечный модуль имеет свои преимущества:

  • Без использования переменной DEBUG, на консоль ничего не выводится.
  • Можно выборочно отлаживать части кода.
  • Подсвеченный вывод в терминал.

Давайте посмотрим на их официальный пример:

// app.js
var debug = require('debug')('http')
  , http = require('http')
  , name = 'My App';

// фальшивое приложение
debug('booting %s', name);

http.createServer(function(req, res){
  debug(req.method + ' ' + req.url);
  res.end('hello\n');
}).listen(3000, function(){
  debug('listening');
});

// фальшивый worker
require('./worker');

// worker.js
var debug = require('debug')('worker');

setInterval(function(){
  debug('doing some work');
}, 1000);

Если запустить пример командой node app.js, ничего не произойдет, но если включить флаг DEBUG, начинается магия:

10 самых популярных ошибок Node-разработчиков

Помимо приложений, debug также можно использовать для небольших модулей, опубликованных в npm. В отличие от более сложного регистратора он выполняет только отладочную работу и делает это хорошо.

Комментарии

ВАКАНСИИ

Добавить вакансию
Разработчик C++
Москва, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ