Як створити потоки з рядка в 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.