Code Monkey home page Code Monkey logo

from2's Introduction

from2 Flattr this!experimental

from2 is a high-level module for creating readable streams that properly handle backpressure.

Convience wrapper for readable-stream's ReadableStream base class, with an API lifted from from and through2.

Usage

from2

stream = from2([opts], read)

Where opts are the options to pass on to the ReadableStream constructor, and read(size, next) is called when data is requested from the stream.

  • size is the recommended amount of data (in bytes) to retrieve.
  • next(err) should be called when you're ready to emit more data.

For example, here's a readable stream that emits the contents of a given string:

var from = require('from2')

function fromString(string) {
  return from(function(size, next) {
    // if there's no more content
    // left in the string, close the stream.
    if (string.length <= 0) return next(null, null)

    // Pull in a new chunk of text,
    // removing it from the string.
    var chunk = string.slice(0, size)
    string = string.slice(size)

    // Emit "chunk" from the stream.
    next(null, chunk)
  })
}

// pipe "hello world" out
// to stdout.
fromString('hello world').pipe(process.stdout)

stream = from2.obj([opts], read)

Shorthand for from2({ objectMode: true }, read).

createStream = from2.ctor([opts], read)

If you're creating similar streams in quick succession you can improve performance by generating a stream constructor that you can reuse instead of creating one-off streams on each call.

Takes the same options as from2, instead returning a constructor which you can use to create new streams.

See Also

  • from2-array - Create a from2 stream based on an array of source values.
  • from2-string - Create a stream from a string. Sugary wrapper around from2.

License

MIT. See LICENSE.md for details.

from2's People

Contributors

bcomnes avatar grncdr avatar hughsk avatar mafintosh avatar max-mapper avatar ryanramage avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

from2's Issues

FYI: native Node JS replacement

from2 generates a readable stream from a function, this can be easily replaced by native Node JS code
This will save you a dependency and make your code more future proof

For simple iterators there is stream.Readable.from(iterable[, options]) since Node 10.17.0

For more complex cases one can use async generators. E.g. see https://2ality.com/2019/11/nodejs-streams-async-iteration.html

If your project depends on a third party module that still uses from2 and you wish to the features of a modern readable stream (e.g. async iteration) then you can use:

const legacyStream = thirdPartyStreamUsingFrom2
const modernStream = (new Readable({objectMode:true}).wrap(legacyStream))

Hope this helps someone ;-)

Does it work?

Just tried your string example and I get:

node_modules/from2/node_modules/readable-stream/lib/_stream_readable.js:520
  dest.on('unpipe', onunpipe);
       ^
TypeError: Cannot call method 'on' of undefined

may unexpectedly block event loop

'use strict'

var from = require('from2')
var tick = 0
var stream = from.obj(function (size, cb) {
  cb(null, {tick: tick++})
})

stream.once('data', function (data)  {
  console.log(data)
})

setTimeout(function () {
  process.exit()
}, 1000)

Expected behavior to print out {"tick": 0} and for the process to exit after 1000 milliseconds, instead the process never exits because the (infinite) from stream is blocking the event loop (even though we use once)

from2 doesn't work without through2

I try it and get infinite loop in headStream

var headStream = from2.obj(function (size, next) {
    var file = new File({
        path: 'head.js',
        contents: new Buffer('head content')
    });
    next(null, file);
})

series(headStream, otherStream)
    .pipe(g.concat(name))
    .pipe(gulp.dest(destPath))

I try it and get 16 loops (size = 16), but head file doesn't write to output file

var flag = false;
var headStream = from2.obj(function (size, next) {
    if (flag) return this.push(null);
    ...
    flag = true;
    next(null, file);
})
...

If I use through it works!

var headStream = from2.obj(function (size, next) {
    ...
    this.push(file)
    next();
})
.pipe(through2.obj(function (file, enc, callback) {
    callback(null, file);
}));
...

Why? And how to do it without through?

If an instance of an Error object is returned in the data then emit that object as an error

stream errors are tricky. I always forget to test for them. from2 would be super handy if it could emit error if an Error object is introduced in the array. For example

from2.obj([
  {a: 1},
  new Error('oooops'),
  {a: 2}
]).on('error', console.log)
 .pipe(ndjson.stringify())

I can take a crack at this, as I believe I would just add a contains instance of Error in the check function
https://github.com/hughsk/from2/blob/master/index.js#L57

@mafintosh does this sound reasonable?

RangeError: Maximum call stack size exceeded

Calling next seems to re-call the read function immediately, adding to the call stack. This limits the logic you can put into the read function,

For example, the below program ends up throwing RangeError: Maximum call stack size exceeded, rather than adding a single character to the stream and then outputting Hello to the console.

var from = require('from2');
var source = from(function(size, next) {
  next(null, '-');
  next(null, null);

  // Never gets here
  console.log('Hello');
});
source.read();

I'm certainly not an expert on Node streams, but this seems unexpected to me.

Edit: Tweaked the code to make it slightly more realistic

Change the expected behavior of next under no arguments

I use your library to wrap internally an async method.

The problem that I detected is related when the case that I need to add more than one chunk of data in the same read call. Check this code:

return from(function (size, next) {
    if (hashFetch()) {
      fetchTweets(function (err, tweets) {// 'tweets' is an array

        next(null, tweets) // works but the final output is { [], [], []}
        // We need something like {[, , , ]}

        tweets.forEach(function (tweet) {
          next(null, tweet) // FAIL, EOF stream!!!!! also with setImmediate
        })

the solution was this:

          // trick to avoid push undefined
          var lastTweet = tweets.pop()
          tweets.forEach(function (tweet) {
            _this.push(tweet)
          })

          next(null, lastTweet) // necessary to avoid push 'undefined'

view source

for me it's ok, but do you think that could be possible support internally this?
Is necessary put undefined under next() call?

Support async iteration

Hello! I've noticed that streams created with this library do not support async iteration. This appears to be because it depends on readable-stream v2.x.x, and Symbol.asyncIterator was added to that library in v3.x.x, when it started supporting Node v10. I noticed in #21 that there was reluctance to upgrade to readable-stream v3.x.x, but that was more than a year and a half ago.

Would you be open to a PR that updates the dependency? Or does that issue still remain? Alternatively, any suggestions for how I might support async iteration in streams created by this library?

Add a way to expose the internal readable stream

I'm the author of fetch-timeline that uses your library yo fetch user timeline from Twitter :D

I detected a case of use where is not possible listen for event different than data if you don't listen data first. Check this tests:

    timeline
      .on('data', function () {}) // necessary to be possible access rs stream :-(
      .on('err', done)
      .on('fetched', function (fetched) {

code source

Could be possible adapt how to expose the readableStream to avoid it?

Proposal: next(null, null) -> next(null) to close a stream

summary
I think we should loosen the close signature of next(null, null) to also accept next(null, undefined).

motivation
Intuitively I assumed the closing signature would be next(null), as is the case with stream.Readable which requires this.push(null) to close a stream. I found it confusing that the signature was instead next(null, null).

detailed design
Probably all that needs to happen is change a strict index.js#L62 from if (data === null) to if (!data).

drawbacks
The only caveat I can think of with a falsy check is that doing cb(null, 0) won't work, but that shouldn't be an issue with streams (e.g. it's generally only buffers / strings / objects).
Maybe a slight impact on performance would happen (falsy check over strict equality OR an extra if statement), but I think the overhead would be negligable.

What do you think?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.