вторник, 4 октября 2016 г.

Шпаргалка про Node.js

// В Node.js process - это аналог window в браузере

process.env.production === window.env.production

global === window

global.process
global.__filename
global.__dirname
global.module.exports
global.require()

global.console()
global.setImmediate()
global.setTimeout()
global.setInterval()

// process

proces.argv
process.env
process.arch
process.versions
process.pid
process.nextTick() - срабатывает до вызова все асинхронных ивентов, находящихся в очереди событий
process.cwd()
process.uptime()
process.memoryUsage()
process.exit()
process.on('exit')

// Схожесть асинхронных взаимодействий браузера и Node.js

// AJAX
$.post('/resource.json', function (data) {
    console.log(data);
});

// Read file
var fs = require('fs');
fs.readFile('./resource.json', function (er, data) {
    console.log(data);
});

// Server response
var http = require('http');
    , server = http.createServer();
server.on('request', function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
})
server.listen(3000);
console.log('Server running at http://localhost:3000/');


// Stream read file
var stream = fs.createReadStream('./resource.json')
stream.on('data', function (chunk) {
    console.log(chunk)
});
stream.on('end', function () {
    console.log('finished')
});

// Создание полноценного сервера

var http = require('http')
    , fs = require('fs')
    , path = require('path')
    , mime = require('mime')
    , cache = {};

function send404 (response) {
    response.writeHead(404, {'Content-Type': 'text/plain'});
    response.write('Error 404: resource not found.');
    response.end();
}

function sendFile (response, filePath, fileContents) {
    response.writeHead(200, {"content-type": mime.lookup(path.basename(filePath))});
    response.end(fileContents);
}

function serveStatic (response, cache, absPath) {
    if (cache[absPath]) {
        sendFile(response, absPath, cache[absPath]);
    } else {
        fs.exists(absPath, function (exists) {
            if (exists) {
                fs.readFile(absPath, function (err, data) {
                    if (err) {
                        send404(response);
                    } else {
                        cache[absPath] = data;
                        sendFile(response, absPath, data);
                    }
                });
            } else {
                send404(response);
            }
        });
    }
}

var server = http.createServer(function (request, response) {
    var filePath = false;
    if (request.url == '/') {
        filePath = 'public/index.html';
    } else {
        filePath = 'public' + request.url;
    }
    var absPath = './' + filePath;
    serveStatic(response, cache, absPath);
});

server.listen(3000, function () {
    console.log("Server listening on port 3000.");
});

// Запуск сервера: node server.js

// Создание Socket.IO сервера

var chatServer = require('./lib/chat_server');
chatServer.listen(server);

var socketio = require('socket.io')
    , io
    , guestNumber = 1
    , nickNames = {}
    , namesUsed = []
    , currentRoom = {};

exports.listen = function (server) {
    io = socketio.listen(server);
    io.set('log level', 1);
    io.sockets.on('connection', function (socket) {
        guestNumber = assignGuestName(socket, guestNumber, nickNames, namesUsed);
        joinRoom(socket, 'Lobby');
        handleMessageBroadcasting(socket, nickNames);
        handleNameChangeAttempts(socket, nickNames, namesUsed);
        handleRoomJoining(socket);
        socket.on('rooms', function() {
            socket.emit('rooms', io.sockets.manager.rooms);
        });
        handleClientDisconnection(socket, nickNames, namesUsed);
    });
};

function assignGuestName (socket, guestNumber, nickNames, namesUsed) {
    var name = 'Guest' + guestNumber;
    nickNames[socket.id] = name;
    socket.emit('nameResult', {
        success: true,
        name: name
    });
    namesUsed.push(name);
    return guestNumber + 1;
}

function joinRoom (socket, room) {
    socket.join(room);
    currentRoom[socket.id] = room;
    socket.emit('joinResult', {room: room});
    socket.broadcast.to(room).emit('message', {
        text: nickNames[socket.id] + ' has joined ' + room + '.'
    });
    var usersInRoom = io.sockets.clients(room);
    if (usersInRoom.length > 1) {
        var usersInRoomSummary = 'Users currently in ' + room + ': ';
        for (var index in usersInRoom) {
            var userSocketId = usersInRoom[index].id;
            if (userSocketId != socket.id) {
                if (index > 0) {
                    usersInRoomSummary += ', ';
                }
                usersInRoomSummary += nickNames[userSocketId];
            }
        }
        usersInRoomSummary += '.';
        socket.emit('message', {text: usersInRoomSummary});
    }
}

function handleNameChangeAttempts (socket, nickNames, namesUsed) {
    socket.on('nameAttempt', function(name) {
        if (name.indexOf('Guest') == 0) {
            socket.emit('nameResult', {
                success: false,
                message: 'Names cannot begin with "Guest".'
            });
        } else {
            if (namesUsed.indexOf(name) == -1) {
                var previousName = nickNames[socket.id];
                var previousNameIndex = namesUsed.indexOf(previousName);
                namesUsed.push(name);
                nickNames[socket.id] = name;
                delete namesUsed[previousNameIndex];
                socket.emit('nameResult', {
                    success: true,
                    name: name
                });
                socket.broadcast.to(currentRoom[socket.id]).emit('message', {
                    text: previousName + ' is now known as ' + name + '.'
                });
            } else {
                socket.emit('nameResult', {
                    success: false,
                    message: 'That name is already in use.'
                });
            }
        }
    });
}

function handleMessageBroadcasting(socket) {
    socket.on('message', function (message) {
        socket.broadcast.to(message.room).emit('message', {
            text: nickNames[socket.id] + ': ' + message.text
        });
    });
}

function handleRoomJoining(socket) {
    socket.on('join', function(room) {
        socket.leave(currentRoom[socket.id]);
        joinRoom(socket, room.newRoom);
    });
}

function handleClientDisconnection(socket) {
    socket.on('disconnect', function() {
        var nameIndex = namesUsed.indexOf(nickNames[socket.id]);
        delete namesUsed[nameIndex];
        delete nickNames[socket.id];
    });
}

// Обработка ошибок при использовании слушателей событий

var events = require('events')
    , myEmitter = new events.EventEmitter();
myEmitter.on('error', function (err) {
    console.log('ERROR: ' + err.message);
});
myEmitter.emit('error', new Error('Something is wrong.'));

process.on('uncaughtException', function (err) {
    console.error(err.stack);
    process.exit(1);
});

// Полноценный сервер

var http = require('http');

var server = http.createServer(function (request, response) {
    if (request.method === 'POST') {
        request.setEncoding('utf8');
        request.on('data', function (chunk) {console.log('Parsed chunk: ' + chunk);});
        request.on('end', function () {
            var body = 'Hello World!';
            response.setHeader('Content-Length', Buffer.byteLength(body));
            response.setHeader('Content-Type', 'text/plain; charset="utf-8"');
            response.statusCode = 302;
            response.writeHead(200, {'Cache-Control': 'no-cache'});
            response.write(body);
            response.end('OK');
        });
    }
});

server.listen(8080);

// Простейший сервер для статичных файлов

var http = require('http')
    , url = require('url')
    , path = require('path')
    , fs = require('fs');

var server = http.createServer(function (request, response) {
    var filePath = path.join(__dirname, url.parser(request.url).pathname);
    response.end(fs.readFileSync(filePath));
});

server.listen(8080);

// Простейший сервер для статичных файлов с потоками

var http = require('http')
    , url = require('url')
    , path = require('path')
    , fs = require('fs');

var server = http.createServer(function (request, response) {
    var filePath = path.join(__dirname, url.parser(request.url).pathname)
        , readStream = fs.createReadStream(filePath);
    readStream.on('data', function (chunk) {
        response.write(chunk);
    });
    readStream.on('end', function () {
        response.end();
    });
});

server.listen(8080);

// Копирование файлов через потоки

var fs = require('fs');

var readStream = fs.createReadStream('./original.txt')
    , writeStream = fs.createWriteStream('./copy.txt');

readStream.pipe(writeStream);

// Запись потока из браузера в файл на сервере

var http = require('http')
    , fs = require('fs');

var server = http.createServer(function (request, response) {
    var writeStream = fs.createWriteStream('./file.txt');
    request.pipe(writeStream);
});

server.listen(8080);

// Передача потока из файла в браузер

var http = require('http')
    , url = require('url')
    , path = require('path')
    , fs = require('fs');

var server = http.createServer(function (request, response) {
    var readStream = fs.createReadStream(path.join(__dirname, url.parse(request.url).pathname));
    readStream.pipe(response);
    readStream.on('error', function (error) {
        response.statusCode = 500;
        response.end('Internal Server Error');
    });
});

server.listen(8080);

// Проверка наличия файла на жестком диске на сервере

var server = http.createServer(function (request, response) {
    var filePath = path.join(__dirname, url.parser(request.url).pathname);
    fs.stat(filePath, function (error, stat) {
        if (error) {
           if (error.code === 'ENOENT') {
                response.statusCode = 404;
                response.end('Not Found');
            } else  {
                response.statusCode = 500;
                response.end('Internal Sever Error');
            }
        } else  {
            response.setHeader('Content-Length', stat.size);
            var readStream = fs.createReadStream(filePath);
            readStream.pipe(response);
            readStream.on('error', function (error) {
                response.statusCode = 500;
                response.end('Internal Server Error');
            });
        }
    });
});

// 2 типа данных присылаемых из форм со стороны браузера
- application/x-www-form-urlencoded - просто данные из полей формы
- multipart/form-data - отправка из формы файла, не ASCII-данных или бинарных данных

// Парсинг данных из формы, присылаемых браузером в формате application/x-www-form-urlencoded

var querystring = require('querystring');

var server = http.createServer(function (request, response) {
    var data = ''
        , items = [];
    request.setEncoding('utf8');
    request.on('data', function (chunk) { // 'item=take' + 'ferrets' + 'to' + 'the' + 'vet'
        data += chunk;
    });
    request.on('end', function () {
        var object = querystring.parse(data);
        items.push(object.item);
    });
});

// Парсинг файла из формы, присылаемого браузером в формате multipart/form-data

var formidable = require('formidable');

var server = http.createServer(function (request, response) {
    var type = request.headers['content-type'] || '';
    if (type.indexOf('multipart/form-data')) {
        var form =  new formidable.IncomingForm();
        form.on('field', function (field, value) {
            console.log(field);
            console.log(value);
        });
        form.on('file', function (name, file) {
            console.log(name);
            console.log(file);
        });
        form.on('end', function () {
            response.end('upload complete');
        });
        form.on('progress', function (bytesReceived, bytesExpected) {
            var percent = Math.floor(bytesReceived/ bytesExpected * 100);
            console.log(percent + '%');
        });
        form.parse(request, function (error, fields, files) {
            console.log(field);
            console.log(files);
            response.end('upload complete');
        });
    }
});

// Создание HTTPS сервера с ключами и сертификатами

var https = require('https')
    , fs = require('fs');

var options = {
      key: fs.readFileSync('./key.pem')
    , cert: fs.readFileSync('./key-cert.pem')
};

var server = https.createServer(function (request, response) {
    response.writeHead(200);
    response.end('Hello HTTPS');
});

server.listen(443);

// Принудительная переадресация клиента из HTTP в HTTPS

http.createServer(function (request, response) {
    response.writeHead(301, {Location: 'https://' + request.headers.host + request.url});
    response.end();
}).listen(80);

для express код будет такой

var app = express();

app.get('*', function (request, response) {
    response.redirect('https://' + request.headers.host + request.url);
});

app.listen(80);

// Проверка на наличие файла на жестком диске

var fs = require('fs');

var filePath = './file.txt';

var fileData;

fs.exists(filePath, function (exists) {
    if (exists) {
        fs.readFile(filePath, 'urf8', function (error, data) {
            if (error) {throw error;}
            data = data.toString();
            fileData = JSON.parse(data || '[]');
        });
    } else {
            fileData = [];
    }
});

// Запись в файл

var fs = require('fs');

var filePath = './file.txt';

var fileData = [1, 2, 3];

fs.writeFile(filePath, JSON.stringify(fileData), 'utf8', function (error) {
    if (error) {throw error;}
    console.log('File saved');
});

// Переименование файла и копирование файла

fs.rename('C:\\hello.txt', 'D:\\hello.txt', function (error) {
    if (error.code === 'EXDEV') {
        console.log('EXDEV');
    }
});

// Вырезание и перемещение файла

module.exports = function move (oldPath, newPath, callback) {
    fs.rename(oldPath, newPath, function (error) {
        if (error) {
            if (error.code === 'EXDEV') {
                copy();
            } else {
                callback(error);
            }
            return;
        }
        callback();
    });
    function copy () {
        var readStream = fs.createReadStream(oldPath);
        var writeStream = fs.createWriteStream(newPath);
        readStream.on('error', callback);
        writeStream.on('error', callback);
        readStream.on('close', function () {
            fs.unlink(oldPAth, callback);
        });
        readStream.pipe(writeStream);
    }
};

move('./copy.js', './copy.js.bak', function (error) {
    if (error) {throw error;}
});

// Наблюдение за изменением файла

var fs = require('fs');

fs.watchFile('./system.log', function (currentState, previousState) {
    if (currenState.mtime.getTime() !== priviousState.mtime.getTime()) {
        console.log('File has been modified');
    }
});

// Получение данных из Basic authorization

function restrict (req, res) {
    var authorization = req.headers.authorization;
    if (!authorization) {throw new Error('Unauthorized');}
    var parts = authorization.split(' ');
    var scheme = parts[0];
    var auth = new Buffer(parts[1], 'base64').toString().split(':');
    var user = auth[0];
    var pass = auth[1];
}

// Создание Socket-сервера

var http = require('net');

var socketServer = net.createServer(function (socket) {
    socket.on('data', function (data) {
        console.log(data);
    });
    soket.on('end', function () {
       console.log('socket has ended');
    });
    socket.on('close', function () {
        console.log('client disconnected');
    });
    socket.on('error', function (error) {
        console.log('Error: ' + error);
    });
    socket.on('connect', function () {
        socket.write('Hello World');
        socket.end();
    });
});

socketServer.listen(1337);

Туннелирование Socket

soket.pipe(socket);

// Создание Socket-клиента

var net = require('net');

var socketClient = net.connect({host: process.argv[2], port: 22});

socketClient.setEncoding('utf8');

socketClient.once('data', function (chunk) {
    console.log('Data: ' + chunk.trim());
    socketClient.end();
});

socketClient.on('connect', function () {
    process.stdin.pipe(socketClient);
    socketClient.pipe(process.stdout);
    process.stdin.resume();
});

socket.on('end', function () {
    process.stdin.pause();
});

// Работа с базой данных MySQL

var mysql = require('mysql');

var db = mysql.createConnection({
      host: '127.0.0.1'
    , user: 'myuser'
    , password: 'mypassword'
    , database: 'timetrack'
});

db.query(
       "CREATE TABLE IF NOT EXISTS work ("
    + "id INT(10) NOT NULL AUTO_INCREMENT, "
    + "hours DECIMAL(5,2) DEFAULT 0, "
    + "date DATE, "
    + "archived INT(1) DEFAULT 0, "
    + "description LONGTEXT,"
    + "PRIMARY KEY(id))"
    , function (error) {
        if (error) {throw error;}
        console.log('Table created');
      }
);

db.query(
      "INSERT INTO work (hours, date, description)  VALUES (?, ?, ?)"
    , [work.hours, work.date, work.description]
    , function (error) {
        if (error) {throw error;}
        console.log('Data inserted');
      }
);

db.query(
      "DELETE FROM work WHERE id=?"
    , [work.id]
    , function (error) {
        if (error) {throw error;}
        console.log('Data deleted');
    }
);

db.query(
      "UPDATE work SET archived=1 WHERE id=?"
    , [work.id]
    , function (error) {
        if (error) {throw error;}
        console.log('Data updated');
    }
);

db.query(
      "SELECT * FROM work WHERE archived=? ORDER BY date DESC"
    , [1]
    , function (error, rows) {
        if (error) {throw error;}
        rows.forEach(function (row) {
            console.log(row.id);
        });
    }
);

// Работа с данными в оперативной памяти через Redis

var redix = require('redis');

var redisClient = redis.createClient(6379, '127.0.0.1');

redisClient.on('error', function (error) {
    console.log('Error: ' +  error);
});

redisClient.set('color', 'red', redis.print);
redisCLient.get('color', function (error, value) {
    if (error) {throw error;}
    console.log('Value: ' + value);
});

redisClient.hmset(
      'camping'
    , {
          'shelter': '2-person tent'
        , 'cooking': 'campstove'
      }
    , redis.print
);
client.hget(
      'camping', 'cooking'
    , function (error, value) {
        if (error) {throw error;}
        console.log('Value: ' + value);
      }
);
client.hkeys(
      'camping'
    , function (error, keys) {
        if (error) {throw error;}
        keys.forEach(function (key) {
            console.log('' + key);
        });
      }
);

// Работа с базой данных MongoDB

var mongodb = require('mongodb')
    , mongoServer = new mongodbServer('127.0.0.1', 27017, {})
    , mongoClient = new mongodbClient('mydatabase', mongoServer, {w: 1});

mongoClient.open(function (error) {
    if (error) {throw error;}
    mongoClient.collection('test_insert', function (error, collection) {
        if (error) {throw error;}
        console.log('We are now able to perform queries.');
        collection.insert(
            {
                  "title": "I like cake"
                , "body": "It is quite good."
              }
            , {safe: true}
            , function (error, documents) {
                if (error) {throw error;}
                console.log('Document ID is: ' + documents[0]._id);
              }
        );
    });
});

// Работа с MongoDB через Mongoose

var mongoose = require('mongoose')
   , db = mongoose.connect('mongodb://localhost/tasks');

var Schema = mongoose.Schema;

var Tasks = new Schema({
    project: String,
    description: String
});

mongoose.model('Task', Tasks);

var Task = mongoose.model('Task');
var task = new Task();

task.project = 'Bikeshed';
task.description = 'Paint the bikeshed red.';

task.save(function (error) {
    if (error) {throw error;}
    console.log('Task saved.');
});

var Task = mongoose.model('Task');

Task.find(
      {'project': 'Bikeshed'}
    , function (error, tasks) {
        for (var i = 0; i < tasks.length; i++) {
            console.log('ID:' + tasks[i]._id);
            console.log(tasks[i].description);
        }
      }
);

var Task = mongoose.model('Task');

Task.update(
      {_id: '4e65b793d0cf5ca508000001'}
    , {description: 'Paint the bikeshed green.'}
    , {multi: false}
    , function (error, rows_updated) {
        if (error) {throw error;}
        console.log('Updated.');
      }
);

var Task = mongoose.model('Task');

Task.findById('4e65b3dce1592f7d08000001', function (error, task) {
    task.remove();
});

mongoose.disconnect();

// Управление process

process.argv.forEach(function (parameter) {
    console.log(parameter);
});

var debug;

if (process.env.DEBUG) {
    debug = function (data) {console.error(data);};
} else {
    debug = function () {};
}

// >>> DEBUG=1 node server.js

process.on('exit', function () {
    console.log('Done');
});

process.on('uncaughtException', function (error) {
    console.log('Got error: ' + error.message);
    process.exit(1);
});
throw new Error('uncaught error');

process.on('SIGINT', function () {
    console.log('Pressed Ctrl-C');
    server.close();
});

// Создание дочерних процесс child_process

var child_process = require('child_process');

// Выполнение команды в окне с командной строкой - вы выполняете команду и ожидаете от нее только финальный ответ, не заботясь о доступе к данным из потока stdout дочернего процесса
// То есть exec - выполнил команду и получил итоговый результат
// Например вы создали бота, который ожидает команд от пользователя и вы выполняете команды пользователя в отдельном процессе process.exec(command).
child_process.exec('command', {timeout: 1500}, function (error, stdout, stderr) {
    if (error) {
        console.log(error);
    } else {
        console.log(stdout);
    }
});

// Выполнение файла
child_process.execFile('ls', ['-l', process.cwd()], function (error, stdout, stderr {
    if (error) {throw error;}
    console.error(stdout);
});

// Выполняет дочерний процесс и возвращает дочерний объект, через который можно этим процессом управлять
var spawnedProcess = child_process.spawn('ls', ['-l']);
spawnedProcess.stdout.pipe(fs.createWriteStream('./result.txt'));
spawnedProcess.on('exit', function (code, sigal) {
    console.log('Done');
});
spawnedProcess.stdin - writable Stream
spawnedProcess.stdout - readable Stream
spawnedProcess.stderr - readable Stream

// Запускает отдельный экземпляр Node.js и возвращает дочерний объект, через который можно этим процессом управлять с помощью посылки сообщений message
// Отличается от spawn возможностью слушать события process.on('message') посылать их с помощью process.send()
var forkedProcess = child_process.fork(__filename, [someData]);
forkedProcess.on('message', function (message) {
   console.log(message);
});

// Работа с stdin, stdout

process.stdout('What is your age?');

process.stdin.setEncoding('utf8');

process.stdin.on('data', function (data) {
    console.log(data);
    process.stdin.pause();
});

process.stdin.resume();

// Глобальное окружение и модули

console.log(__dirname);
console.log(__filename);
console.log(process.cwd());
console.log(process.env);
console.log(process.argv);
console.log(new Buffer('from string'));

exports.a = function a () {};

module.exports = {a: a};

var a = require('a');

console.log(require.resolve('a')); // узанать имя загружаемого модуля

if (require.resove('http') === 'http') {
    http.createServer(...);
}

// Удаление загруженного модуля

delete require.cache[require.resolve('./my-file.js');]; // Возвращает true, если модуль выгружен

// Группировка модулей для загрузки их с помощью одного require() разом

Файл index.js с загрузкой всех модулей

var http = require('http')
    , net = require('net')
    , url = require('url')
    , querystring = require('querystring')
    , path = require('path')
    , fs = require('fs');

module.exports = {
      http: http
    , net: net
    , url: url
    , querystring: querystring
    , path: path
    , fs: fs
};

или можно так

module.exports = {
      http: require('http')
    , net: require('net')
    , url: require('url')
    , querystring: require('querystring')
    , path: require('path')
    , fs: require('fs')
};

var modulesGroup = require('./group.js');

modulesGroup.http.createServer();

// Работа со stdin и stdout

process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.on('data', function (text) {
    process.stdout.write(text.toUpperCase());
});

// Форматирование вывода на консоль

console.log('Hello %s %d %j', 'string', 3.14, {name: 'John'});

%s - String
%d - Number
%j - JSON

// Вывод теущего stack trace

console.trace();

// Измерение времени выполнения программы

console.time('label');
console.endTime('label');

// Данные об операционной системе

console.log(process.arch);
console.log(process.platform);

switch (process.arch) {
    case 'x64': require('./lib.x64.js'); break;
    case: 'ia32': require('./lib.Win32.js'); break;
    default: throw new Error('Unsupported arch: ' + process.arch);
}

console.log(process.memoryUsage);

// Получение параметров командной строки

process.argv.slice(2).forEach(function (parameter) {
    console.log(parameter);
});

// Выход из программы

process.exit(1);

process.on('exit', function () {
    console.log('bye');
});

// Обработка сигналов, пришедщих от других процессов

process.stdin.resume();
process.on('SIGHUP', function () {
    console.log('Reloading configuration...');
});
console.log('PID: ' + process.pid);
process.kill(pid, signal);

// Выполнение действий с задержкой в следущем цикле

process.nextTick(function () {
    console.log('After stack was clear');
});

// Буферы

var buf = new Buffer(255);
buf[0] = 15;
console.log(Buffer.isBuffer(buf)); // true
console.log(buf);
console.log(buf.toString());

buf = new Buffer('some string', 'base64');
console.log(but.toString('utf8'));

// Конвертация бинарных файлов базы данных в читаемые строки

var fs = require('fs');

fs.readFile('./words.dbf', function (error, buffer) {
    if (Buffer.isBuffer(buffer)) {
        console.log(buffer.toString('utf8'));
    }
});

// Создание событий

var events = require('events');

class MusicPlayer extends events.EventEmitter {
    constructor () {
        super();
        this.playing = false;
    }
}

var player = new MusicPlayer();

player.on('play', function (track) {
    this.playing = true;
});

player.on('stop', function () {
    this.playing = false;
});

player.once('pause', function () {
    this.playing = false;
});

player.on('error', function (error) {
    console.error('Error: ' + error);
});

player.emit('play', 'Queen');

player.emit('error', 'unable to play');

setTimeout(function () {
    player.emit('stop');
}, 1000);

player.removeListener('play');
player.removeListener('stop');

// Использование модуля domain для глобальной обработки асинхронных ошибок

var domain = require('domain');
var events = require('events');

var playerDomain = domain.create();

playerDomain.on('error', function (error) {
    console.log('playerDomain error: ' + error);
});

playerDomain.run(function () {
    var player = new MusicPlayer();
    player.play();
});

Любые события сгенерированны player будут перехвачены domain и особенно сообщения об ошибках error

domain.run(function () {
    fs.open(function (error) {
        if (error) {throw error;} -> Domain.eventListener: 'error'
    });
})

// Типы потоков Stream

stream.Readable -> _read(size)
stream.Writable -> _write(chunk, encoding, callback)
stream.Duplex -> _read(size), _write(chunk, encoding, callback)
stream.Transform -> _flush(size), _transform(chunk, encoding, callback)

// Наследование потоков

var stream = require('stream');

class StatStream extends stream.Readable {
    constructor (limit) {
        super();
        this.limit = limit;
    }
    _read (size) {
        if (this.limit === 0) {
            this.push(); // Done
        } else {
           this.push(process.memoryUsage());
           this.limit--;
        }
    }
}

server.get('/', function (request, response) {
    var statStream = new StatStream(10);
    statStream.pipe(response);
});

// Используйте потоки вместо буферов - жто быстрее и расходует меньше пямяти

Для сравнения это

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

http.createServer(function(req, res) {
    fs.readFile('./somefile.js', function(err, data) {
        res.writeHead(200);
        res.end(data);
    });
}).listen(8000);

и это

http.createServer(function(req, res) {
   fs.createReadStream('./static_buffered.js').pipe(res);
}).listen(8000);

// Работа с файлами и папками

var fs = require('fs');

fs.readdir('./path/to/directory', function (error, files) {
    if (error) {throw error;}
    console.log(files); // ['file1', 'file2', 'file3']
});

Функции для работы с файлами:

fs.rename - переименовать и переместить файл
fs.truncate - обрезать или увеличть файл до требуемого размера
fs.ftruncate - тоже, что и truncate, но принимает файловый дескриптор (переменную с файлом)
fs.chown - изменить владельца файла или владеющую группу
fs.fchown - тоже, что и chown, но принимает файловый дескриптор (переменную с файлом)
fs.lchown - тоже, что и chown, но не следует по символьным ссылкам
fs.chmod - изменить права доступа к файлу
fs.fchmod - тоже, что и chmod, но принимает файловый дескриптор (переменную с файлом)
fs.lchmod -  тоже, что и chmod, но не следует по символьным ссылкам
fs.stat - получить статус по состоянию файла
fs.lstat - тоже, что и stat, но возвращает информацию о ссылке на файл, если она приведена нежели на то, куда указывает ссылка
fs.fstat - тоже, что и stat, но принимает файловый дескриптор (переменную с файлом)
fs.link - создает жесткую ссылку на файл
fs.symlink - создает символическую ссылку на файл
fs.readlink - читает значение символической ссылки
fs.realpath - возвращает абсолютный путь до файла
fs.unlink - удаляет файл
fs.rmdir - удаляет директорию
fs.mkdir - создает директорию
fs.readdir - считывает список файлов и папок в директории
fs.close - закрывает файл, удаляя файловый дескриптор (переменную с файлом)
fs.open - открывает файл для чтения или создает новый файл для записи в него
fs.utimes - уставнваливает дату последнего открытия и модификации файла
fs.futimes - тоже, что и utimes, но принимает файловый дескриптор (переменную с файлом)
fs.fsync - синхронизирует данные файла с жестким диском
fs.write - записывает данные в файл
fs.read - считывает данные из файла

// Работа с файлами через потоки Stream

var fs = require('fs');
var readable = fs.createReadStream('./original.txt');
var writeable = fs.createWriteStream('./copy.txt');
readable.pipe(writeable);

// Функция для чтения файла целиком, записи данных в файл и добавления данных в конец файла

fs.readFile()
fs.writeFile()
fs.appendFile()

// Функции слежения за изменением файлов

var fs = require('fs');

fs.watch('./watch-directory'. console.log);
fs.watchFile('./watch-file.txt', console.log)

// Ловля ошибок при работе с файлами синхронными методами

var fs = require('fs');

try {
    var file = fs.readFileSync('./file.txt');
} catch (error) {
    console.log(error);
}

// Ссылки на открытые файлы - файловые дескрипторы - числа

var fileSecriptor = fs.openSync('./file.txt', 'a');

console.log(typeof fileDescriptor === 'number'); // true

// Получение информации о размере файла

var fs = require('fs');

fs.stat('/etc/passwd', function(err, stats) {
    if (err) {console.log(err.message); return; }
    console.log(stats);
    // console.log('this file is ' + stats.size + ' bytes long.');
});

// Работа с DNS-сервером

vart dns = require('dns');

dns.lookup('www.google.com', function (error, address) {
    if (error) {console.error('Error: ' + error);}
    console.log('Addresses: ' + address);
});

// Как правильно создавать дочерний процесс

                                                                                               ------------------------------
                                                                                              | Родительский процесс |
                                                                                               ------------------------------
                                                                                                                   |
                                                                        ------------------------------   |   --------------------------
Тип программы?                                           | Не Node.js-программа |<-->| Node.js-программа |
                                                                        ------------------------------        --------------------------
                                                                                                    |                                          |
                                                                             ---------------   |    -------------------------     |        
Тип интерфейса?                                               | Streaming |<-->| Buffered (callback) |     |  Streaming
                                                                              ---------------        -------------------------     |
                                                                                             |                         |                         |
                                                                                        -------         -------   |   ------          -------
Используется оболочка типа командной строки?    | Нет |        | Нет |<-->| Да |          | Нет |
                                                                                        -------         -------       ------           -------
                                                                                             |                 |              |                   |
                                                                                     ----------    ------------     ---------     --------
Используйте метод:                                                   | spawn |   | execFile |    | exec |       | fork |
                                                                                     ----------    ------------     ---------     --------

1) child_process.execFile() - выполняет стороннюю не Node.js программу с переданным набором аргументов и возвращает из неё ответ в формате Buffer после того, как та завершит свое выполнение.

2) child_process.spawn() - выполняет стороннюю не Node.js программу с переданным набором аргументов и предоставляет стриминговый интерфейс для передачи и вхождения данных, а также вызывает события, когда процесс выполнения завершится.

3) child_process.exec() - выполняет одну или более команд внутри оболчки командной строки и возвращает ответ в формате Buffer после того, как выполнение команд завершится.

4) child_process.fork() - выполняет сторонний модуль Node.js в отдельном процессе Node.js переданным набором аргументов и предоставляет стриминговый интерфейс для передачи и вхождения данных, а также вызывает события, как метод child_process.spawn(), но также предоставляет и interprocess
communication (IPC) какнал для передачи сообщений между родительским процессом process и дочерним процессом child_process.

// Выполнить файл сторонней программы из Node.js и получить в ответ результат выполнения в виде Buffer

var child_process = require('child_process');

child_process.execFile('./echo.php', ['hello', 'world'], function (error, stdout, stderr) {
    if (error) {console.log(error.code);}
    console.log('stdout: ' + stdout);
    console.log('stderr: ' + stderr);
});

// Запустить стороннюю программу, которая в ответ отдает много данных, которые лучше получать через поток чтения

var child_process = require('child_process');

var childProcess = child_process.spawn('./echo.php', ['hello', 'world']);

childProcess.on('error', console.error);

childProcess.stdout.pipe(process.stdout);
childProcess.stderr.pipe(process.stderr);

var cat = cp.spawn('cat', ['messy.txt'])
    , sort = cp.spawn('sort')
    , uniq = cp.spawn('uniq');

// Output of each command
// becomes input for next command

cat.stdout.pipe(sort.stdin);
sort.stdout.pipe(uniq.stdin);
uniq.stdout.pipe(process.stdout);

// Выполнить набор команд в оболочке командной строки: cmd.exe

var child_process = require('child_process');

child_process.exec('fins messy.txt | sort | uniq', function (error, stdout, stderr) {
     console.log(stdout);
});

// Осоединение дочернего процесса от основного процесса
// Когда у вас есть долго работающий процесс, который начал Node.js, но вы хотите выйти из основного процесса, но так, чтобы дочерний процесс при этом продолжил работать.

var childProcess = child_process('./longrun.php', [], {detached: true});

// Выполнение стронних Node.js программ в качестве дочерних процессов - child_process.fork() работает аналогично WebWorkers в браузере

var child_process = require('child_process');

var childProcess = child_process.fork('./file.js');

process.send('message'); >>> childProcess.on('message', function (msg) {});
childProcess.send('mesage'); >>> process.on('message', function (msg) {});

childProces.once('error', function (error) {
    callback(error);
    childProces.kill();
});

child.once('exit', function (code, signal) {
    callback(new Error('Child exited with code: ' + code));
});

childProcess.disconnect();

// Взаимодействие отдельных процессов child_process.fork()

Parent:

var fork = require('child_process').fork;

var child = fork('./child.js');

child.on('message', function(msgobj) {
    console.log('Parent got message:', msgobj.text);
});

child.send({text: "I love you"});

Child:

process.on('message', function(msgobj) {
    console.log('Child got message:', msgobj.text);
    process.send({text: msgobj.text + ' too'});
});

>>> Child got message: I love you
>>> Parent got message: I love you too

Parent:

var fork = require('child_process').fork
    , net = require('net')
    , children = [];

require('os').cpus().forEach(function (f, idx) {
    children.push(fork("./child.js", [idx]));
});

net.createServer(function(socket) {
    var rand = Math.floor(Math.random() * children.length);
    children[rand].send(null, socket);
}).listen(8080);

Child:

var id = process.argv[2];

process.on('message', function (n, socket) {
    socket.write('child ' + id + ' was your server today.\r\n');
    socket.end();
});

>>> Trying 127.0.0.1...
>>> ...
>>> child 3 was your server today.
>>> Connection closed by foreign host.

// Узнать чесло процессоров в системе

var os = require('os');

console.log(os.cpus().length);

// Программа рестартующая Node.js сервер при именении файлов

Файл restarter.js

var fs = require('fs');

var exec = require('child_process').exec;

function watch() {
    var child = exec('node server.js');
    var watcher = fs.watch(__dirname + '/server.js', function(event) {
        console.log('File changed, reloading.');
        child.kill();
        watcher.close();
        watch();
    });
}

watch();

Файл server.js

var http = require('http');

var server = http.createServer(function(req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('This is a super basic web application');
});

server.listen(8080);

// Обработка ошибок сервера с использованием domain

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

var d = domain.create();

d.run(function () {
    var server = http.createServer(req, res) {
        d.on('error', function (er) {
            res.statusCode = 500;
            res.end('internal server error');
            server.close();
            setTimeout(process.exit, 5000, 1);
        });
        response.end('hello world');
    });
    server.listen(3000);
});

Другой пример

var domain = require('domain');

var dom = domain.create();

dom.on('error', function (error) {
    console.error('error', error.stack);
});

dom.run(function () {
    throw new Error("my domain error");
});

// error Error: my domain error
// at /js/basicdomain.js:10:8
// ...

Еще пример

var somefunc = function () {
    throw new Error('Explicit bind error');
};

dom.add(somefunc);

dom.run(function () {
    somefunc();
});

// [Error: Explicit bind error]

И еще пример

var domain = require('domain');

var fs = require('fs');

var dom = domain.create();

dom.on('error', function (error) {
    console.error('error', error.stack);
});

fs.readFile('somefile', dom.bind(function (err, data) {
    if (error) {throw new Error('bad file call');}
}));

// { [Error: bad call]
// domain_thrown: true,
// ...

Перемещение между доменами

dom1.add(aFuncThatThrows);

dom1.run(function () {
    dom1.exit();
    dom2.enter();
    aFuncThatThrows();
});

// domain 2 error

// Запуск интерпретатора Node из самого Node

Файл repl-basic.js

var repl = require('repl');
repl.start({
      input: process.stdin
    , output: process.stdout
});

$ node repl-basic.js
> 10 + 20
30
>

var net = require('net');
var repl = require('repl');

net.createServer(function (socket) {
    var r = repl.start({
          input: socket
        , output: socket
        , terminal: true
    });
    r.on('exit', function() {
        socket.end();
    });
}).listen(1337);

console.log('node repl listening on 1337');

// Кластеры Node.js - как workers в браузере

Можно задействовать в программе все ядры процессора

var os = require('os');

os.cpus().length; // 4 ядра

var http = require('http');

var cluster = require('cluster');

if (cluster.isMaster) {
    var totalWorkers = require('os').cpus().length - 1; // CPU cores
    console.log('Start running %d workers', totalWorkers);
    for (var i = 0; i < totalWorkers; i++) {
        cluster.fork();
    }
    cluster.on('online', function (worker) {
        console.log('Worker ' + worker.process.pid + ' is online.');
    });
    cluster.on('exit', function (worker, code, signal) {
        console.log('Worker ' +  worker.process.pid + ' died with code: ' + code + ' and signal: ' + signal);
        console.log('Startting a new worker');
        cluster.fork();
    });
} else if (cluster.isWorker) {
    console.log('Worker PID:' + process.pid);
    http.createServer(function (request, response) {
       response.writeHead(200);
       response.end('Process ' + process.pid + ' says hello1' + ' cluster worker: ' + cluster.worker.id);
    }).listen(3000);
}

Эта программа выполняет разные вещи в зависимости от того является ли данный процесс мастер процессом или дочерним процессом.
При первом выполнении она является мастер процессом, который выявляется значением cluster.isMaster.
Когда мастер процесс вызывает метод cluster.fork(), то таже самая программа ответвляет себя в качестве дочернего процесса и в этом случае данное дитя занимает под свое выполнение отдельное ядро процессора.
Когда данныая программа повторно выполняется в ответвленном процессе, то значение cluster.isWorker равно true  и новый HTTP-сервер запускается на общем порту port. То есть несколько отдельных процессов разделяют нагрузку на 1 сервер.

Теперь каждый запрос сервера автоматом будет обрабытываться в отдельном ядре процессора.

Присоединение к серверу через браузер позволит увидеть уникальный идентификатор каждого процесса, например cluster.woker.id = Hello from 8.
Балансировка нагрузки по всем воркерам выполняется автоматически, то есть обновление страниы в браузере несколько раз покажет, что её обслуживают каждый раз разные воркеры с разынми ID>


// Загрузка опциональных модулей

try {
    var client = require('hiredis'); // super fast!
} catch (e) {
    var client = require('./lib/redis'); // fast
}
module.exports = client;

// Использования разных режимов работы кода

process.env.NODE_ENV = 'development';

if (process.env.NODE_ENV === 'development') {
    log = function (message) {console.log(message);};
} else {
    log = function () {};
}

// Динамическое изменение состава модуля при его импорте - monkey patching

Файл patcher.js

// ./logger - это другой модуль, содержимое которого мы динамически изменяем

require('./logger').customMessage = function() {
    console.log('This is a new functionality');
};

Файл main.js

require('./patcher');

var logger = require('./logger');

logger.customMessage(); // Новая пропатченная функция

// Паттерны

- Observer
- Faсade
- Factory
- Proxy
- Decorator
- Adapter
- Strategy
- State
- Template
- Middleware
- Command

// Паттер Обозреватель

document.addListener('click', function () {});
document.removeListener('click', function () {});
document.emit('click', function () {});

// Паттерн Фабрика - для создания разных объектов

function createImage (name) {
             if (name.match(/\.jpeg$/)) {return new JpegImage(name);
    } else if (name.match(/\.gif$/)) {return new GifImage(name);
    } else if (name.match(/\.png$/)) {return new PngImage(name);
    } else {throw new Exception('Unsupported format');
    }
}

Паттерн Прокси - для валидации передаваемых данных или логирования



function proxy (object) {
    return {
        say: function (word) {
            console.log(word);
            if (word.length > 10) {throw new Error('bad word');}
            object.say(word);
        }
    };
}

var object = {
    say: function (word) {
        alert(word);
    }
};

proxy(object).say('hello');

// Паттерн Декоратор - расширяет возможности объекта, но не изменяет исходный объект

function decorate (object) {
    return Object.assign({}, object, {say: function (word) {alert(word);}});
}

var object = {
    run: function () {alert('Running!');}
};

var otherObject = decorate(object);

otherObject.say('hello');

// Паттерн Адаптер - позволяет получить досутп к функциональности объект через другой интерфейс - это как розетка с разными вилками

fs.writeFile = function (filename, contents, options, callback) {
    if (typeof options === 'function') {
        callback = options;
        options = {};
    } else if (typeof options === 'string') {
        options = {encoding: options};
    }
    database.put(path.resolve(filename), contents, {valueEncoding: options.encoding}, callback);
}

fs.writeFile('file.txt', 'Hello!', function() {
    fs.readFile('file.txt', {encoding: 'utf8'}, function (err, res) {
        console.log(res);
    });
});

// Паттерн Стратегия - берет объект и смотрит на область окружения, в которой он выполняется, по которой он выбирает что далее с этим объектом надо делать

function Config(strategy) {
    this.data = {};
    this.strategy = strategy;
}

Config.prototype.get = function (path) {
    return objectPath.get(this.data, path);
}

Config.prototype.set = function (path, value) {
    return objectPath.set(this.data, path, value);
}

Config.prototype.read = function (file) {
    console.log('Deserializing from ' + file);
    this.data = this.strategy.deserialize(fs.readFileSync(file, 'utf-8'));
}

Config.prototype.save = function (file) {
    console.log('Serializing to ' + file);
    fs.writeFileSync(file, this.strategy.serialize(this.data));
}

// Паттер Состояние - меняет состояние системы

function FailsafeSocket(options) {
    this.options = options;
    this.queue = [];
    this.currentState = null;
    this.socket = null;
    this.states = {
          offline: new OfflineState(this)
        , online: new OnlineState(this)
    }
    this.changeState('offline');
}

// Паттерн Шаблонный метод - реализация подобных методов у других объектов

JsonConfig.prototype._deserialize = function(data) {
    return JSON.parse(data);
};

JsonConfig.prototype._serialize = function(data) {
    return JSON.stringify(data, null, ' ');
}

// Паттерн Прослойка - выполнение дейсвий между операциями

function (request, response, next) {
    ...
    next();
}

app.use(middleware(request, response));

// Паттерн Команда - объект содержит команду, которую потому он должен выполнить

function createTask (target, args) {
    return function() {
       target.apply(null, args);
    }
}

var task = createTask(function (message) {console.log(message);}, 'hello');

task();

// Работа с модуляеми

// Модуль синглтон

// 'db.js' module
module.exports = new Database('my-app-db');

// 'app.js' module

var db = require('./db');

global.db = new Database('my-app-db');

// Вызыван ли файл другим модулем или он работает в одиночку

function run () {
    console.log('код модуля');
}

if (module.parent) {
    console.log('Меня загрузил другой файл');
    module.exports.run = run;
} else {
    console.log('Я работаю один');
    run();
}

Комментариев нет:

Отправить комментарий