Новые возможности io.js, которые Вы возможно не используете

в 8:50, , рубрики: ES6, io.js, javascript, node.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)

Всего понемногу

Несколько интересных возможностей, на которые стоит обратить внимание:

  1. Вы можете прописать require (‘./’) с помощью require (‘.’) (1.6.2).
  2. При использовании console.log или util.inspect, ES6 объекты Promise, Map и Set имеют красивый вид (2.0.0).
  3. os.tmpdir () работает одинаково хорошо на всех операционных системах (2.0.0). Ранее, некоторые операционные системы выдавали косые слэши, не говоря уже о других ошибках. Теперь Вы никогда не столкнетесь с этой проблемой.
  4. Множество других улучшений: парсинг строки запроса(query string) (1.6.1), меньшее использование tls памяти (2.0.0), более быстрый process.nextTick (2.0.0) и util.format для отдельных аргументов (2.1.0).
  5. Постепенное улучшение безопасности io.js. При обмене ключами по методу Диффи-Хеллмана, параметры должны быть не менее 1024 бит (2.1.0).

Где найти больше информации?

По моему мнению лучшую информацию можно найти на GitHub в ChangeLog. Также io.js ведет официальный блог на Medium, на котором можно найти информацию о последних обновлениях платформы и о проекте в целом.

Автор: Марк Хартер (Marc Harter)
Над переводом работали: greebn9k(Сергей Грибняк), Роман Сенин, Андрей Хахарев
Singree

Автор: greebn9k

Источник

Поделиться новостью

* - обязательные к заполнению поля