Гоголев Сергей
Вычисление числа Фибоначчи
– CPU bound операция
Подсчёт количества строк в файле
– I/O bound операция
Сохранение промежуточных вычислений
– Memory bound операция
Чтение HTTP запроса | I/O |
Парсинг HTTP запроса | CPU |
Запрос к базе данных | I/O |
Запрос к API | I/O |
Генерация HTML | CPU |
Отправка HTML | I/O |
Одновременно выполняется
только одна операция
I/O Чтение 1 Кб данных c SSD – 0.0014 ms
CPU 28 000 циклов на одном 2Ghz ядре
I/O Сетевое соединение – 60 ms
CPU 132 000 000 циклов на одном 2Ghz ядре
Apache использует multithreading
Nginx использует паттерн reactor
Ryan Dahl
epoll
kqueue
I/O Completion Port API
About libuv, Bert Belder
Помимо ресурса необходимо указать обработчик результата – callbaсk
var data = request('https://api.github.com/');
var result = writeFile(file, data);
console.info(result);
request('https://api.github.com/', function (err, data) {
writeFile(file, data, function (err, result) {
console.info(result);
});
}
1. Callbacks come last
2. Error comes first
✓ Функции первого класса и замыкания
✓ Готов к EventLoop (DOM events, setTimeout)
✓ Большое комьюнити !
API работы с файловой системой – fs, для запросов – http, логирования – console
Node.js
Original Node.js presentation, Slides,
Ryan Dahl
Understanding Node.js Event Loop,
Tamas Kadlecsik
How does NodeJS work,
Eugene Obrezkov
apt-get install nodejs
brew install node
Скачать с nodejs.org
$ node
> 2 + 2
4
v6 LTS – длительная поддержка
v7 Current – самые новые возможности
// hypotenuse.js
function square(n) {
return n * n;
}
function calculateHypo(a, b) {
return Math.sqrt(square(a) + square(b));
}
// hypotenuse.js
// module = {
// filename: '/absolute/path/to/hypotenuse.js',
// exports: {}
// }
function square(n) {
return n * n;
}
module.exports.calculate = function (a, b) {
return Math.sqrt(square(a) + square(b));
}
// return module.exports;
// index.js
var hypotenuse = require('./hypotenuse.js')
hypotenuse.calculate(3, 4); // 5
// hypotenuse.js
function square(n) {
return n * n;
}
module.exports = function (a, b) {
return Math.sqrt(square(a) + square(b));
}
// index.js
var hypotenuse = require('./hypotenuse.js');
hypotenuse(3, 4); // 5
module.exports = 42; // Число
function Student(name) {
this.name = name;
}
Student.prototype.getName = function() {
return this.name;
};
module.exports = Student; // Конструктор
module.exports = new Student('Billy'); // Объект
// hypotenuse.js
function square(n) {
return n * n;
}
module.exports.calculate = function (a, b) {
return Math.sqrt(square(a) + square(b));
}
exports.calculate = function (a, b) {
return Math.sqrt(square(a) + square(b));
}
exports ⇢ module.exports
// hypotenuse.js
function square(n) {}
exports = function (a, b) {
return Math.sqrt(square(a) + square(b));
}
// index.js
var hypotenuse = require('./hypotenuse.js');
hypotenuse(3, 4); // hypotenuse is not a function
exports x module.exports
var url = require('url');
url.parse('https://yandex.ru/');
{
protocol: 'https:',
host: 'yandex.ru',
port: null,
path: '/'
}
var lodash = require('lodash');
lodash.shuffle([1, 2, 3, 4]);
// [4, 1, 3, 2]
lodash.uniq([2, 1, 2]);
// [2, 1]
var counter = 1;
module.exports = function() {
return counter++;
};
var counter = require('./counter');
var anotherCounter = require('./counter');
console.info(counter()); // 1
console.info(counter()); // 2
console.info(anotherCounter()); // ?
console.info(anotherCounter()); // 3
Модули импортируются один раз, и после первого require экспорт кешируется
Результат хранится в свойстве require.cache
{
'/absolute/path/to/filename.js': {
filename: '...',
exports: {},
}
}
Если есть встроенный модуль с таким именем, экспортируется он – require('url')
Если имя начинается с ./, / или . ./, экспортируется по указанному пути – require('./index.js') или require('../index.js')
В противном случае пакет ищется в node_modules начиная с текущей директории и поднимаясь вверх – require('lodash')
/home/gogoleff/hypotenuse/node_modules
/home/gogoleff/node_modules
/home/node_modules
Полный алгоритмNode Package Manager
Инструмент командной строки, устанавливается вместе с Node.js
Глобальное хранилище модулей
registry.npmjs.org/lodash
Создаёт файл-манифест package.json, описывающий модуль
Модуль + манифест = пакет
Файл содержит название и версию нашего модуля, а так же его зависимости
Demo
Ищет пакет в хранилище по имени
Выводит информацию о пакете по имени
Demo
Устанавливает пакет в качестве зависимости в директорию node_modules
Если у зависимости есть подзависимости – установятся в node_modules у зависимости
node_modules └── mathjs@3.8.0 └──node_modules └─┬ ├── complex.js@4.0.3 ├── fraction.js@1.7.0 ├── basic-auth@1.0.3 └── tiny-emitter@1.0.1
Устанавливает определённую версию пакета
Зависимость зафиксируется в package.json
Demo
Устанавливает пакет в качестве зависимости, которая не требуется для работы модуля
2.7.0
major – новые возможности без сохранения обратной совместимости
minor – новые возможности с сохранением обратной совместимости
patch – исправления ошибок, рефакторинг
semver.org{ "dependencies": { "express": "1.2.3", "express": ">1.2.3", "express": ">=1.2.3", "express": "~1.2.3", // >=1.2.3 <1.3.0 "express": "^1.2.3", // >=1.2.3 <2.0.0 "express": "1.2.*", "express": "latest", "express": "git://github.com/expressjs/express.git", "express": "git://github.com/expressjs/express.git#4.13.4", "express": "git://github.com/expressjs/express.git#master", "express": "git://github.com/expressjs/express.git#f3d99a4", "express": "expressjs/express#f3d99a4" } }Advanced Range Syntax
save=true // Всегда фиксировать зависимость
save-exact=true // Строго фиксировать версию
init-author-name='Sergey Gogolev'
docs.npmjs.com/files/package.json
NPM — найдётся подходящий модуль, Всеволод Струкчинский
var EventEmitter = require('events').EventEmitter;
var emitter = new EventEmitter();
emitter.on('log', console.info);
emitter.emit('log', 'Hello!'); // Hello!
emitter.emit('unknown event'); // Do nothing
emitter.emit('error');
// Uncaught, unspecified "error" event.
Задача: принимать запросы и отвечать на них
var http = require('http');
var server = new http.Server();
server.on('request', function (req, res) {
res.end('Hello, User!');
});
server.listen(8080);
server.on('request', function (req, res) {
console.info(req.method); // GET
});
req.headers; // {'accept-encoding': 'gzip'}
req.url; // /favicon.ico
server.on('request', function (req, res) {
console.info(res.statusCode); // 200
});
res.setHeader('content-type', 'text/html');
res.write('Hello!');
res.end();
Demo
Задача: cделать запрос и прочитать ответ
var http = require('http');
var req = http.request({
hostname: 'localhost',
port: 8080
});
req.on('response', function (response) {
var body = '';
response.on('data', function (chunk) {
body += chunk; // res.write(chunk);
});
response.on('end', function () {
console.info(body); // res.end();
});
});
req.end();
Demo
url.parse('https://yandex.ru/');
// {
// protocol: 'https:',
// host: 'yandex.ru',
// path: '/',
// ...
// }
url.format({
protocol: 'https:',
host: 'yandex.ru'
});
// https://yandex.ru/
querystring.parse('foo=bar&arr=a&arr=b');
// {
// foo: 'bar',
// arr: ['a', 'b']
// }
querystring.stringify({
foo: 'bar',
arr: ['a', 'b']
});
// foo=bar&arr=a&arr=b
var fs = require('fs');
fs.readFile(__filename, function (err, content) {
console.info(content);
});
__filename – строка, которая хранит абсолютный путь до текущего файла
Buffer 63 6f 6e 73 74 20 66 73 20 3d 20 72 65 ...
Для работы с бинарными данными
Буфер можно рассматривать как массив чисел, ограниченных диапазоном 0-255
Каждое число представляет байт
var letterB = new Buffer([98]);
console.info(letterB.toString()); // b
console.info(letterB.toString('utf-8')); // b
var msg = new Buffer([0x2f, 0x04, 0x3d, 0x04,
0x34, 0x04, 0x35, 0x04, 0x3a, 0x04, 0x41, 0x04]);
msg.toString(); // Default: utf8
// \u0004=\u00044\u00045\u0004:\u0004A\u0004
msg.toString('ucs2');
// 'Яндекс'
fs.readFile(__filename, function (err, data) {
console.info(data.toString());
});
fs.readFile(__filename, 'utf-8', function (err, data) {
console.info(data);
});
fs.appendFile();
fs.writeFile();
fs.unlink();
fs.mkdir();
fs.stat(__filename, function (stats) {
console.info(stat.isDirectory()); // false
});
fs.watch();
fs.watch(__filename, function (event, filename) {
console.info(event); // change or rename
});
fs.watch(__dirname, function (event, filename) {
console.info(event); // change or rename
});
fs.readFileSync(__filename);
fs.writeFileSync(__filename, data);
fs.mkdirSync('/games/diablo3');
Блокирует поток выполнения программы