[Перевод] Новые возможности io.js, которые Вы возможно не используете
Оригинал: New io.js Features You May Not Be Using
Платформа io.js развивается быстрыми темпами, оказывая большое влияние на всю экосистему Node. За короткое время в io.js было внесено множество исправлений, улучшена производительность, а также добавлены новые возможности и функции.
Если Вы не следили за развитием io.js, ничего страшного — в данной статье мы вкратце представим основные возможности платформы. Рассматривать их мы будет по мере выхода, то есть от старых к новым. Где это будет нужным, дадим ссылки на соответствующую документацию (на английском языке). Описывая каждую новую функциональность, будем указывать ее первую версию. Код написан с использованием стандартных функций ES6, которые доступны в io.js. Итак, начнем…
ES6 возможности (1.0.0)Первоначально одной из задач разработчиков io.js было приблизиться к функциональности V8, а именно: полноценно использовать все имеющиеся возможности ES6. Считалось, что встроенные по умолчанию генераторы, так же, как и другие функции ES6, будут работать достаточно стабильно и без флагов. Советуем не спешить с поиском информации по ES6 — для начала ознакомьтесь с документацией, ссылки на которую мы дадим в статье.
Ссылка на документацию: Функции ES6
Отметим: По умолчанию в Node 0.12 присутствуют некоторые функции ES6. Под флагом --harmony можно найти еще больше функций. Однако, работа и доступность функций ES6 отличается от io.js (например: в Node генераторы по умолчанию не присутствуют).
Подробная документация по ошибкам (1.2.0) Когда-нибудь задавались вопросом, что такое EPIPE, EMFILE, ENOENT? Я лично задавал себе этот вопрос и не раз. Слава богу, теперь в io.js появилась значительно улучшенная документация по ошибкам.
Ссылка на документацию: Документация по ошибкам
Простая реализация потоков (1.2.0) Формально, когда необходимо применить поток, то Вы расширяете базовый поток и применяете один или несколько методов, в зависимости от типа потока. Например, поток Transform предполагает использование метода _transform и метода _flush опционально. На примере ниже видно, как поток Stream разбивает файл по строкам: var stream = require ('stream') var liner = new stream.Transform ({ objectMode: true }) liner._transform = function (chunk, enc, done) { var data = chunk.toString () if (this._lastLineData) { data = this._lastLineData + data } var lines = data.split ('\n') this._lastLineData = lines.splice (lines.length-1,1)[0] lines.forEach (this.push.bind (this)) done () } liner._flush = function (done) { if (this._lastLineData) { this.push (this._lastLineData) } this._lastLineData = null done () } module.exports = liner Со всеми «кишками» метода наружу код выглядит довольно неопрятно. Теперь Вы можете передать эти методы в качестве опций в конструктор. Просто уберите знак нижнего подчеркивания (_). Liner позволяет изменить код (с использованием нотации объекта из расширений в ES6) и получить следующее: 'use strict' const stream = require ('stream') let liner = new stream.Transform ({ // Include any existing constructor options objectMode: true, // This is the _transform method transform (chunk, enc, done) { let data = chunk.toString () if (this._lastLineData) { data = this._lastLineData + data } let lines = data.split ('\n') this._lastLineData = lines.splice (lines.length — 1, 1)[0] lines.forEach (this.push.bind (this)) done () }, // This is the _flush method flush (done) { if (this._lastLineData) { this.push (this._lastLineData) } this._lastLineData = null done () } }) module.exports = liner Ссылка на документацию: Конструктор потокаВозможность просмотра всех IP адресов для домена (1.2.0) Если бы Вы прописали команду dns.lookup в ранних версиях io.js, то Вам бы выдали только первый адрес. Сейчас же есть опция {all: true}, которая позволяет получить целый массив адресов. 'use strict' const dns = require ('dns') // Returns first address dns.lookup ('google.com', console.log) // => '173.194.46.40' 4 // Returns all resolved addresses in an array dns.lookup ('google.com', { all: true }, console.log) /* => [ { address: '173.194.46.40', family: 4 }, { address: '173.194.46.38', family: 4 }, … { address: '2607: f8b0:4009:804::1007', family: 6 } ] */ Ссылка на документацию: О dns.lookupСобытие unhandleRejection (1.4.1) Если отклонить (reject) promise, но об этом никто никогда не узнает, можно ли считать, что его действительно отклонили? Конечно это произошло, но опять же — об этом никто не узнает! Как можно догадаться, здесь таится корень появления множества сложных ошибок в коде. Для примера обратите внимание на код ниже: 'use strict' let delay = function (ms) { return new Promise (function (resolve) { setTimeout (resolve, ms) }) } delay (2000) .then (function (data) { data.foo = 'hello' }) Видите ошибку? Функция delay возвращает Promise, который будет выполнен через определенное время, а когда он будет выполнен, получим undefined и ReferenceError. Это произойдет, потому что мы попытались получить доступ к свойству foo со значением undefined. Между тем, мы никогда не узнаем, что произошло, и будем с удивлением чесать в затылке, потому что на необработанные отказы никто не обратил внимание. Хотя в некоторых библиотеках, возможность такого поведения promise прописана, в ES6 Вы информации об этом не найдете. К счастью, Вы можете провести проверку кода с помощью события unhandledRejection через process. process.on ('unhandledRejection', function (er) { console.log ('got unhandled rejection', er.stack) }) Если Вы используете promise в ES6, то рекомендуем настроить unhandledRejection, чтобы ни один отказ не остался незамеченным.
Ссылка на документацию: process.on («unhandledRejection»)
Отметим: Также существует событие rejectionHandled. Оно используется в том случае, когда надо разобраться с отказом promise, возникающим в следующем шаге event loop. rejectionHandled полезен, когда необходимо обнулить promise, которые были неверно отсеяны с помощью unhandledRejection. Для более детального описания смотрите документацию.
StreamWrap и JSStream (1.4.1) Начиная с данной версии, между потоками в C++ и JS появилась полноценная связь. Теперь Вы можете использовать стандартный Duplex поток для ввода данных при работе с потоками низкого порядка (например, c программными гнездами) на C++. Метод tls.connect () использует эту особенность в полной мере. В качестве примера просмотрите следующий набор тестов.Метод Buffer#indexOf Далее описан удобный метод того, как проводить поиск в буфере с помощью строки, буфера или числа. Метод ведет себя так же, как и Array#indexOf, а именно: он возвращает индекс стартовой позиции первого найденного совпадения в буфере. По желанию вы можете задать стартовый индекс в качестве дополнительного параметра. 'use strict' const assert = require ('assert') let buf = new Buffer ('abc def ghi') assert.equal (buf.indexOf ('abc'), 0) assert.equal (buf.indexOf ('bc'), 1) assert.equal (buf.indexOf ('def'), 4) assert.equal (buf.indexOf ('c'), 2) assert.equal (buf.indexOf ('c', 4), 11) Дополнительный метод lastIndexOf пока находится в обсуждении.
Ссылка на документацию: buffer#indexOf
Модули предварительной загрузки (1.6.0) Теперь Вы можете предварительно загрузить модули во время выполнения скриптов или использования REPL. Сделать это можно с помощью -r или флага --require. Например: iojs -r ./foo -r bar my-app.js То же самое, что и: require ('./foo') require ('bar') require ('./my-app') Предварительная загрузка модулей дает новые возможности использования io.js. Например, Вы можете добавить новую функциональность в полезное работающее приложение. Или: Вы хотите создать приложение, которое будет делать мгновенные снимки состояния неупорядоченного массива для устранения утечки данных. Для этого Вам не надо будет поддерживать такую конфигурацию: if (cfg.useHeapdump) require ('heapdump') Вам надо будет всего лишь запустить приложение с предварительно загруженным модулем тогда, когда Вам нужна требуемая функциональность: iojs -r heapdump app.js Еще один вариант использования — вместе с компиляторами (Babel, CoffeeScript и т.п.). Например, если Вы используете Babel для компиляции кода на ES6 или ES7, необходимо прописать примерно следующее: require ('babel/register') require ('./my-actual-app') Теперь, Вы можете провести настройку прямо из командной строки без использования родного приложения: iojs -r babel/register my-actual-app.js Отметим: На настоящий момент, Babel уже полноценно поддерживает Node. Специальный инструмент позволяет делать описанное выше и даже больше.Устранение ошибок с помощью Synchronous I/O (2.1.0) Хотя Synchonous I/O удобен, в особенности для shell-скриптинга, от него сильно снижается производительность многих приложений (например, серверов). Вы можете поискать Sync в коде, однако что делать, если Вы используете сторонний модуль, который не следует схеме именования? Именно здесь на выручку приходит флаг --trace-sync-io. Когда выполняется синхронный запрос, Вам приходит уведомление о stack trace.
Давайте посмотрим на простой пример:
'use strict' const http = require ('http') const cp = require ('child_process') http.createServer (function (req, res) { let stdout = cp.execFileSync ('whoami') res.end (`${stdout}\n`) }).listen (3000) Имеем HTTP сервер, который обрабатывает синхронный код по каждому запросу. Рекомендовать такой вариант нельзя. Так, если мы выполним iojs --trace-sync-io server.js, а затем перейдем на httpL//localhost:3000, увидим в консоли следующее предупреждение: WARNING: Detected use of sync API at spawnSync (child_process.js:1241:27) at execFileSync (child_process.js:1291:13) at /Users/wavded/Projects/whats-new-iojs/server.js:6:19 at emitTwo (events.js:87:13) at emit (events.js:172:7) at parserOnIncoming (_http_server.js:474:12) at parserOnHeadersComplete (_http_common.js:88:23) at socketOnData (_http_server.js:325:22) at emitOne (events.js:77:13) Всего понемногу Несколько интересных возможностей, на которые стоит обратить внимание:
- Вы можете прописать require (»./») с помощью require (».») (1.6.2).
- При использовании console.log или util.inspect, ES6 объекты Promise, Map и Set имеют красивый вид (2.0.0).
- os.tmpdir () работает одинаково хорошо на всех операционных системах (2.0.0). Ранее, некоторые операционные системы выдавали косые слэши, не говоря уже о других ошибках. Теперь Вы никогда не столкнетесь с этой проблемой.
- Множество других улучшений: парсинг строки запроса (query string) (1.6.1), меньшее использование tls памяти (2.0.0), более быстрый process.nextTick (2.0.0) и util.format для отдельных аргументов (2.1.0).
- Постепенное улучшение безопасности io.js. При обмене ключами по методу Диффи-Хеллмана, параметры должны быть не менее 1024 бит (2.1.0).
Где найти больше информации? По моему мнению лучшую информацию можно найти на GitHub в ChangeLog. Также io.js ведет официальный блог на Medium, на котором можно найти информацию о последних обновлениях платформы и о проекте в целом.
Автор: Марк Хартер (Marc Harter)Над переводом работали: greebn9k (Сергей Грибняк), Роман Сенин, Андрей ХахаревSingree