Страницы

пятница, 24 мая 2013 г.

Понимая Node.

Изложение моего понимания нескольких основных принципов платформы Node.js на простых примерах. Понимание - это процесс. В моем случае процесс сопровождается чтением "Professional Node.js. Building Javascript Based Scalable Software" by Pedro Teixeira.


Неблокирующий код.
Рассмотрим на примере двух HTML-страниц.
Blocking:
<!DOCTYPE html>
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title>Blocking</title>
 </head>
 <body>
  <script type="text/javascript">
   var d = new Date();
   var n = 1000000000;
   document.body.appendChild(document.createTextNode('Посчитаем до ' + n));
   var i = 0;
   while (i < n) i++;
   document.body.appendChild(document.createElement('br'));
   document.body.appendChild(document.createTextNode('i = ' + i));   
   document.body.appendChild(document.createElement('br'));
   var t = new Date() - d;
   document.body.appendChild(document.createTextNode('t = ' + String(t) + ' ms'));
  </script>
 </body>
</html>



Non-blocking:
<!DOCTYPE html>
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title>Non-blocking</title>
 </head>
 <body>  
  <script type="text/javascript">
   var d = new Date();
   var n = 1000000000;
   document.body.appendChild(document.createTextNode('Посчитаем до ' + n));
   setTimeout(function() {
    var i = 0;
    while (i < n) i++;
    document.body.appendChild(document.createElement('br'));
    document.body.appendChild(document.createTextNode('i = ' + i));
   }, 0);
   document.body.appendChild(document.createElement('br'));
   var t = new Date() - d;
   document.body.appendChild(document.createTextNode('t = ' + String(t) + ' ms'));
  </script>  
 </body>
</html>



Замыкания.
В Node (который суть JavaScript) очень часто применяются функции обратного вызова, поэтому использовать контекст выполнения очень удобно.
На примере HTML-страницы выглядит примерно так:
<!DOCTYPE html>
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title>Closure</title>
 </head>
 <body>
  <div id="sq" style="width: 400px; height: 400px; background-color:#ffff00;"></div>
  <script type="text/javascript">
   var i = 1;
   var sq = document.getElementById('sq');
   function step() {
    var h = i.toString(16);
    sq.style.backgroundColor = "#ffff" + h + h;
    if (i < 15) {
     i++;
     setTimeout(step, 300);
    }
   }
   setTimeout(step, 300);
  </script>  
 </body>
</html>

Предотвратить засорение глобального объекта можно следующим образом:
<!DOCTYPE html>
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title>Closure - Non-polluting</title>
 </head>
 <body>
  <div id="sq" style="width: 400px; height: 400px; background-color:#ffff00;"></div>
  <script type="text/javascript">
   (function() {
   var i = 1;
   var sq = document.getElementById('sq');
   function step() {
    var h = i.toString(16);
    sq.style.backgroundColor = "#ffff" + h + h;
    if (i < 15) {
     i++;
     setTimeout(step, 300);
    }
   }
   setTimeout(step, 300);
   })();
  </script>  
 </body>
</html>

Модули.
В Node используется система организации модулей CommonJS, что позволяет избежать проблем, связанных с глобальным объектом, присущих браузерному JavaScript.
mymodule.js:
exports.hello = function() {
 console.log('Hello!');
}

mymodule_require.js:
require('./mymodule').hello();


Callback.
Функции обратного вызова - один из основных паттернов Node. Для примера прочитаем содержимое файла.
Синхронно, fssync.js:
var fs = require('fs');
console.log(fs.readFileSync('mymodule.js').toString());

Асинхронно, fsasync.js:
var fs = require('fs');
fs.readFile('mymodule.js', function(err, content) {
 if (err) throw err;
 console.log(content.toString());
});


Event emitter.
Объект, который (в соответствии с названием) эмитирует события. Один из основных паттернов Node.
event_emitter.js:
var util = require('util'),
EventEmitter = require('events').EventEmitter;
// объект event emitter
var Ticker = function() {
 var self = this;
 setInterval(function() {
  self.emit('tick');
 }, 1000);
};
//Ticker.prototype = EventEmitter;
// класс Ticker наследует от класса EventEmitter, таким образом
// методы прототипа EventEmitter становятся доступны для класса Ticker
util.inherits(Ticker, EventEmitter);

// объект event listener
var ticker = new Ticker();
// можно использовать .addListener, но .on - короче
//ticker.addListener("tick", function() {
ticker.on("tick", function() {
 console.log("tick");
});


p.s. Node.js - это интересно.