简体   繁体   English

如何将节点可读 stream 转换为 RX 可观察

[英]How to convert node readable stream to RX observable

If I have a Node.js stream, say for example from something like process.stdin or from fs.createReadStream , how can I convert this to be an RxJs Observable stream using RxJs5?如果我有一个 Node.js stream,例如来自process.stdinfs.createReadStream类的东西,我如何使用 RxJs5 将其转换为 RxJs Observable stream?

I see that RxJs-Node has a fromReadableStream method, but that looks like it hasn't been updated in close to a year.我看到RxJs-Node有一个fromReadableStream方法,但看起来它已经将近一年没有更新了。

For anyone looking for this, following Mark's recommendation, I adapted rx-node fromStream implementation for rxjs5.对于任何寻找这个的人,按照 Mark 的建议,我为 rxjs5 调整了 rx-node fromStream实现。

import { Observable } from 'rxjs';

// Adapted from https://github.com/Reactive-Extensions/rx-node/blob/87589c07be626c32c842bdafa782fca5924e749c/index.js#L52
export default function fromStream(stream, finishEventName = 'end', dataEventName = 'data') {
  stream.pause();

  return new Observable((observer) => {
    function dataHandler(data) {
      observer.next(data);
    }

    function errorHandler(err) {
      observer.error(err);
    }

    function endHandler() {
      observer.complete();
    }

    stream.addListener(dataEventName, dataHandler);
    stream.addListener('error', errorHandler);
    stream.addListener(finishEventName, endHandler);

    stream.resume();

    return () => {
      stream.removeListener(dataEventName, dataHandler);
      stream.removeListener('error', errorHandler);
      stream.removeListener(finishEventName, endHandler);
    };
  }).share();
}

Note that it intrinsically breaks all back pressure functionalities of streams.请注意,它本质上打破了流的所有背压功能。 Observables' are a push technology. Observables 是一种推送技术。 All input chunks are going to be read and pushed to the observer as quickly as possible.所有输入块都将被读取并尽快推送给观察者。 Depending on your case, it might not be the best solution.根据您的情况,它可能不是最佳解决方案。

The following should work for both v4 and v5 ( disclaimer untested):以下内容适用于 v4 和 v5(免责声明未经测试):

fromStream: function (stream, finishEventName, dataEventName) {
    stream.pause();

    finishEventName || (finishEventName = 'end');
    dataEventName || (dataEventName = 'data');

    return Observable.create(function (observer) {

      // This is the "next" event
      const data$ = Observable.fromEvent(stream, dataEventName);

      // Map this into an error event
      const error$ = Observable.fromEvent(stream, 'error')
        .flatMap(err => Observable.throw(err));

      // Shut down the stream
      const complete$ = Observable.fromEvent(stream, finishEventName);

      // Put it all together and subscribe
      const sub = data$
        .merge(error$)
        .takeUntil(complete$)
        .subscribe(observer);

      // Start the underlying node stream
      stream.resume();

      // Return a handle to destroy the stream
      return sub;
    })

    // Avoid recreating the stream on duplicate subscriptions
    .share();
  },

The answers above will work, though the don't support backpressure.上面的答案将起作用,尽管不支持背压。 If you are trying to read a large file with createReadStream, they will read the whole thing in memory.如果您尝试使用 createReadStream 读取大文件,它们将读取内存中的整个内容。

Here is my implementation with backpressure support: rxjs-stream这是我的背压支持实现: rxjs-stream

RxJs-Node 实现基于 RxJs4,但无需太多工作即可移植到 RxJs5 https://github.com/Reactive-Extensions/rx-node/blob/87589c07be626c32c842bdafa782fca5924e749c/index.js#L52

Since Node v11.14.0 streams support for await https://nodejs.org/api/stream.html#stream_readable_symbol_asynciterator由于 Node v11.14.0 流支持for await https://nodejs.org/api/stream.html#stream_readable_symbol_asynciterator

it means you can pass stream to from() operator.这意味着您可以将流传递给from()运算符。

Under hood rxjs(v7.xx) will call fromAsyncIterable() that will return Observable在引擎盖下 rxjs(v7.xx) 将调用fromAsyncIterable()将返回Observable

Anyone coming here recently (RxJS 7 & Node 18+) should use the following code.最近来到这里的任何人(RxJS 7 和 Node 18+)都应该使用以下代码。

Why does this work?为什么这行得通? RxJS has been updated to handle stream like objects. RxJS 已更新为处理 stream 类对象。 When you pass a ReadStream to RxJS, it tests if it is ReadableStreamLike and then turns it into an AsyncGenerator .当您将 ReadStream 传递给 RxJS 时,它会测试它是否为ReadableStreamLike ,然后将其转换为AsyncGenerator

import { from } from 'rxjs';

const file = fs.createReadStream(fileName);

const file$ = from(file).subscribe({
  next:  (dat) => { ... },
  error: (err) => { ... },
  complete: () => { ... }
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM