Як створити потоки з рядка в Node.Js?


Відповіді:


27

З вузла 10.17, stream.Readable має fromметод легко створювати потоки з будь-якого ітерабельного (який включає літерали масиву):

const { Readable } = require("stream")

const readable = Readable.from(["input string"])

readable.on("data", (chunk) => {
  console.log(chunk) // will be called once with `"input string"`
})

Зауважте, що принаймні між 10,17 та 12,3, рядок сам по собі є ітерабельним, тому він Readable.from("input string")буде працювати, але випромінює одну подію на символ. Readable.from(["input string"])передаватиме одну подію на елемент у масиві (у цьому випадку - один елемент).

Також зауважте, що в пізніших вузлах (ймовірно, 12.3, оскільки документація говорить, що функція була змінена тоді), більше не потрібно обертати рядок у масив.

https://nodejs.org/api/stream.html#stream_stream_readable_from_iterable_options


Відповідно до stream.Readable.from , виклик
абр.

Моє ліжко. Функція була додана в 10,7 і вела себе так, як я описав спочатку. З цього моменту рядки більше не потрібно загортати в масиви (починаючи з 12.3, він більше не повторює кожен символ окремо).
Fizker

186

Як @substack виправив мене в # node , новий API потоків у Node v10 полегшує це:

const Readable = require('stream').Readable;
const s = new Readable();
s._read = () => {}; // redundant? see update below
s.push('your text here');
s.push(null);

… Після чого ви можете безперешкодно подати трубу або іншим чином передати її призначеному споживачеві.

Це не так чисто, як однорівневе відновлення , але це дозволяє уникнути додаткової залежності.

( Оновлення: від v0.10.26 до v9.2.1 поки що, виклик pushбезпосередньо із запиту REPL припиняється, за not implementedвинятком випадків, якщо ви не встановили його _read. Він не завершиться збоями всередині функції або сценарію. Якщо невідповідність робить вас нервувати, включати noop.)


6
З документів (посилання) : "Усі програми для читання потоку повинні читати _readспосіб отримання даних із базового ресурсу."
Фелікс Рабе

2
@eye_mew спочатку потрібно вимагати ("stream")
Jim Jones

8
Чому ви натискаєте nullна буфер потоку?
допатраман

5
@dopatraman nullповідомляє потоку, що він закінчив читати всі дані та закрити потік
chrishiestand

2
Схоже, ви не повинні робити це так. Цитуючи документи : " readable.push()Метод призначений викликати тільки читабельні виконавці, і лише зсередини readable._read()методу."
Аксель Раушмайер

127

Не використовуйте відповідь резюме Джо Лісса. Він працюватиме в більшості випадків, але в моєму випадку він втратив мені непогані 4 або 5 годин пошуку помилок. Для цього немає необхідності в сторонніх модулях.

НОВИЙ ВІДПОВІДЬ :

var Readable = require('stream').Readable

var s = new Readable()
s.push('beep')    // the string you want
s.push(null)      // indicates end-of-file basically - the end of the stream

Це повинен бути повністю сумісний читабельний потік. Дивіться тут для отримання додаткової інформації про правильне використання потоків.

СТАРИЙ ВІДПОВІДЬ : Просто використовуйте рідний потік PassThrough:

var stream = require("stream")
var a = new stream.PassThrough()
a.write("your string")
a.end()

a.pipe(process.stdout) // piping will work as normal
/*stream.on('data', function(x) {
   // using the 'data' event works too
   console.log('data '+x)
})*/
/*setTimeout(function() {
   // you can even pipe after the scheduler has had time to do other things
   a.pipe(process.stdout) 
},100)*/

a.on('end', function() {
    console.log('ended') // the end event will be called properly
})

Зауважте, що подія "закриття" не випромінюється (що не потрібно для інтерфейсів потоку).


2
@Finn Вам не потрібні парени в JavaScript, якщо немає аргументів
BT

не використовуй "var" у 2018 році! але const
stackdave

30

Просто створіть новий примірник streamмодуля та налаштуйте його відповідно до ваших потреб:

var Stream = require('stream');
var stream = new Stream();

stream.pipe = function(dest) {
  dest.write('your string');
  return dest;
};

stream.pipe(process.stdout); // in this case the terminal, change to ya-csv

або

var Stream = require('stream');
var stream = new Stream();

stream.on('data', function(data) {
  process.stdout.write(data); // change process.stdout to ya-csv
});

stream.emit('data', 'this is my string');

13
Цей код порушує правила потоку. pipe()повинен повернути цільовий потік, як мінімум.
greim

2
Кінцева подія не викликається, якщо ви використовуєте цей код. Це не гарний спосіб створити потік, який можна використовувати загалом.
BT

12

Редагувати: відповідь Гарта, ймовірно, краща.

Мій старий текст відповіді збережений нижче.


Щоб перетворити рядок у потік, ви можете використовувати паузу через потік:

through().pause().queue('your string').end()

Приклад:

var through = require('through')

// Create a paused stream and buffer some data into it:
var stream = through().pause().queue('your string').end()

// Pass stream around:
callback(null, stream)

// Now that a consumer has attached, remember to resume the stream:
stream.resume()

Я не міг змусити рішення zeMirco працювати для мого випадку використання, але resumerпрацював досить добре. Дякую!
1313

Пропозиція відновлення @substack для мене спрацювало дуже добре. Дякую!
Гарт Кідд

2
Resumer чудовий, але "автоматичне відновлення потоку на nextTick" може принести сюрпризи, якщо ви очікуєте, що можете передати потік невідомим споживачам! У мене був якийсь код, який передав потік вмісту у файл, якщо збереження db метаданих вдалося. Це була помилка, що ховається, це сталося успішним, коли запис на db негайно повернув успіх! Пізніше я відремонтував речі, що знаходяться всередині блоку асинхронізації, і потік ніколи не читався. Урок: якщо ви не знаєте, хто буде споживати ваш потік, дотримуйтесь техніку through (). Pause (). Queue ('string'). End ().
Jolly Roger

1
Я витратив близько 5 годин налагодження свого коду, тому що я використав частину цієї відповіді. Було б чудово, якби ви захотіли .. видаліть його
BT

10

Для цього є модуль: https://www.npmjs.com/package/string-to-stream

var str = require('string-to-stream')
str('hi there').pipe(process.stdout) // => 'hi there' 

1
Це каламбур на "є додаток для цього"? ;)
masterxilo

1
Посилання в коментарі є корисним: npmjs.com/package/string-to-stream
Dem Pilafian

FYI Я намагався використовувати цю бібліотеку для написання JSON для google drive, але мені це не вийшло. Статтю про це написав тут: medium.com/@dupski/… . Також додано як відповідь нижче
Рассел Бріггс

6

у кавовому сценарії:

class StringStream extends Readable
  constructor: (@str) ->
    super()

  _read: (size) ->
    @push @str
    @push null

використай це:

new StringStream('text here').pipe(stream1).pipe(stream2)

6

Іншим рішенням є передача функції зчитування конструктору "Читабельний" (cf doc- read read-options )

var s = new Readable({read(size) {
    this.push("your string here")
    this.push(null)
  }});

ви можете після використання s.pipe для зразка


яка мета повернення наприкінці?
Кирило Резніков

"завжди повертайте щось (або нічого)", це зразки документації.
Філіп Т.

У JS, якщо функція не має повернення, вона є еквівалентом порожньому поверненню. Чи можете ви надати посилання там, де ви його знайшли?
Кирило Резніков

ви повинні мати рацію. Я сказав, що більше для найкращої практики. Я нічого не хочу повертати, це не помилка. Тож я видаляю рядок.
Філіп Т.

5

Я втомився від необхідності повторно вивчати це кожні півроку, тому я просто опублікував модуль npm, щоб відстежувати деталі реалізації:

https://www.npmjs.com/package/streamify-string

Це ядро ​​модуля:

const Readable = require('stream').Readable;
const util     = require('util');

function Streamify(str, options) {

  if (! (this instanceof Streamify)) {
    return new Streamify(str, options);
  }

  Readable.call(this, options);
  this.str = str;
}

util.inherits(Streamify, Readable);

Streamify.prototype._read = function (size) {

  var chunk = this.str.slice(0, size);

  if (chunk) {
    this.str = this.str.slice(size);
    this.push(chunk);
  }

  else {
    this.push(null);
  }

};

module.exports = Streamify;

strце те, stringщо повинно бути передано конструктору після виклику і буде виведено потоком як дані. options- це типові параметри, які можуть бути передані потоку відповідно до документації .

На думку Travis CI, він повинен бути сумісний з більшістю версій вузла.


2
Коли я опублікував це спочатку, я не включив відповідний код, на який мені сказали, що нахмурився.
Кріс Аллен Лейн

2

Ось акуратне рішення в TypeScript:

import { Readable } from 'stream'

class ReadableString extends Readable {
    private sent = false

    constructor(
        private str: string
    ) {
        super();
    }

    _read() {
        if (!this.sent) {
            this.push(Buffer.from(this.str));
            this.sent = true
        }
        else {
            this.push(null)
        }
    }
}

const stringStream = new ReadableString('string to be streamed...')

1

JavaScript набрав качку, тому якщо ви просто скопіюєте API для читання потоку , він буде працювати чудово. Насправді, ви, ймовірно, не можете реалізувати більшість цих методів або просто залишити їх як заглушки; все, що вам потрібно буде реалізувати, - це те, що використовує бібліотека. Ви також можете використовувати попередньо побудований EventEmitterклас Node для вирішення подій, тому вам не доведеться самостійно реалізовувати addListenerта подібні.

Ось як можна реалізувати його в CoffeeScript:

class StringStream extends require('events').EventEmitter
  constructor: (@string) -> super()

  readable: true
  writable: false

  setEncoding: -> throw 'not implemented'
  pause: ->    # nothing to do
  resume: ->   # nothing to do
  destroy: ->  # nothing to do
  pipe: -> throw 'not implemented'

  send: ->
    @emit 'data', @string
    @emit 'end'

Тоді ви можете використовувати його так:

stream = new StringStream someString
doSomethingWith stream
stream.send()

Я розумію: TypeError: string is not a function at String.CALL_NON_FUNCTION (native) коли я його використовую якnew StringStream(str).send()
pathikrit

Тільки тому, що JavaScript використовує набір качок, не означає, що ви повинні винаходити колесо. Вузол вже забезпечує реалізацію для потоків. Просто створіть новий екземпляр на stream.Readableкшталт @Garth Kidd, який запропонував.
Сукіма

4
@Sukima: stream.Readable не було, коли я писав цю відповідь.
icktoofay
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.