Skip to content

Stream not emitting data event after pipe+unpipe #1041

Description

@dmail

The following should log 'ok' to the console but nothing is logged.

var stream = require('stream');
var pass = new stream.PassThrough();
var writable = new stream.Writable();

pass.pipe(writable); pass.unpipe(writable);
pass.on('data', function(chunk){ console.log(chunk.toString()); });
pass.write('ok');

Of course, commenting the line pass.pipe(writable); pass.unpipe(writable); fix the problem.

Can you explain me why?

Activity

  1. micnic commented on Mar 3, 2015

    @micnic
    Contributor

    you should get the error "chunk is not defined" on line 6, when fixed it should print "ok" on the stdout.

  2. dmail commented on Mar 3, 2015

    @dmail
    Author

    I'm sorry @micnic I forgot the chunk argument when I wrote this snipet I've modified the line.
    When I exec this script nothing is logged to the console, but when I comment the pipe/unpipe line, ok is logged.

    var stream = require('stream');
    var pass = new stream.PassThrough();
    var writable = new stream.Writable();
    
    // When the line below is commented, ok is logged
    //pass.pipe(writable); pass.unpipe(writable); 
    pass.on('data', function(chunk){ console.log(chunk.toString()); });
    pass.write('ok');
  3. reopened this on Mar 3, 2015
  4. micnic commented on Mar 3, 2015

    @micnic
    Contributor

    pass gets paused and it needs to be resumed with pass.resume() before writing. I'm not sure, but this could be a bug, in node 0.10 it works as you expect, in 0.12 and io.js it needs to be resumed.

  5. dmail commented on Mar 3, 2015

    @dmail
    Author

    You're right !

    With 0.10 no need to call resume()
    With 0.12 I have to call resume() to get the 'ok' log

    var stream = require('stream');
    var pass = new stream.PassThrough();
    var writable = new stream.Writable();
    
    pass.pipe(writable); pass.unpipe(writable); 
    pass.resume();
    pass.on('data', function(chunk){ console.log(chunk.toString()); });
    pass.write('ok');

    Thank you very much :)

  6. chrisdickinson commented on Mar 3, 2015

    @chrisdickinson
    Contributor

    .unpipe sets state.flowing = false, which is the same as explicitly .pause()'ing the stream. Since it's in an explicitly paused state, .on('data') does not implicitly resume the stream. This seems like a regression. cc @iojs/streams

  7. added
    confirmed-bugIssues and PRs for confirmed bugs.
    streamIssues and PRs related to Node.js streams.
    on Mar 5, 2015
  8. micnic commented on Mar 10, 2015

    @micnic
    Contributor

    ping @iojs/streams

  9. sonewman commented on Mar 12, 2015

    @sonewman
    Contributor

    @chrisdickinson This "feature" seems to have been introduced here 444bbd4

    streams: Support objects other than Buffers

    ...
    Fixed a bug with unpipe where the pipe would break because
    the flowing state was not reset to false.

    I believe this was during the streams2 era. So in that case I think we weren't expecting to get any data events.

    We'll have to see what the implications are to streams with only a readable listener if we remove this.

  10. Trott commented on Feb 4, 2016

    @Trott
    Member

    @sonewman @chrisdickinson @nodejs/streams I don't suppose there's anything new to add to this issue at this time, is there?

  11. iliakan commented on Feb 9, 2016

    @iliakan

    That's a bug indeed, tracked down in the following:

    var fs = require('fs');
    
    var src = fs.createReadStream(__filename);
    var dst = fs.createWriteStream('/tmp/test');
    
    src.pipe(dst);
    src.unpipe(dst);
    src.on('data', function() {
        console.log("Never happens")
      });

    As @chrisdickinson wrote above, unpipe sets the stream to "explicitly paused" state, then on('data') does not resume it.

    P.S. And yes, extra resume call at the end makes it flow again.

  12. added
    docIssues and PRs related to Node.js documentation.
    and removed
    confirmed-bugIssues and PRs for confirmed bugs.
    on Dec 8, 2016
  13. Fishrock123 commented on Dec 8, 2016

    @Fishrock123
    Contributor

    This sounds like it may just be a docs issue?

  14. iliakan commented on Dec 9, 2016

    @iliakan

    yes, it's a docs issue, there's another one I remember, explicitly citing docs. Maybe close this one.

  15. iliakan commented on Dec 9, 2016

    @iliakan

    at least, it looks like the code won't be fixed, so it becomes a docs issue ;)

  16. jasnell commented on May 30, 2017

    @jasnell
    Member

    ping @nodejs/streams

  17. mcollina commented on May 30, 2017

    @mcollina
    SponsorMember

    It is a doc issues, specifically we need to update https://nodejs.org/api/stream.html#stream_three_states so that it clearly states things.

  18. mcollina commented on Jun 5, 2017

    @mcollina
    SponsorMember

    Fixed in 0b432e0.

  19. mpotra commented on Oct 15, 2017

    @mpotra
    Contributor

    Actually, this still breaks any data even listeners added before any pipe() / unpipe(), which I believe is a serious behavior flaw introduced in Readable by 444bbd4

    Consider the following:

    const readable; // A Readable stream
    const writable; // A Writable stream
    
    readable.on('data', function onData(chunk) {
      // This will only execute until unpipe();
    });
    
    readable.pipe(writable);
    readable.unpipe(writable);

    Given it unexpectedly pauses the stream, even while there are data listeners attached, this cannot be just a docs issue.

    In scenarios where there are two independent algorithms consuming the stream - the first using data listeners and the second using pipe - it is unfeasible for the first algorithm to monitor any following pipe/unpipe (events are on writable anyway)


    For the scenario I mentioned here, I've already created a quick fix (and associated test), but I'm waiting for your thoughts before turning it into a PR.

    The fix simply checks if there are any remaining data listeners upon cleanup() after calling unpipe(); if there are, it keeps the stream in flowing mode.


    A similar argument could be made for attaching data listeners after unpipe(), but that involves a slightly different fix, which requires only setting state.flowing to null instead of false (here and here) in the unpipe() function.
    That would ensure the stream is in paused mode after unpipe() and no data listeners, but that it resumes flow whenever there's a new consumer attached.
    I'd still need to run tests for this, and it might also mean reverting the docs update

  20. mcollina commented on Oct 16, 2017

    @mcollina
    SponsorMember

    Given it unexpectedly pauses the stream, even while there are data listeners attached, this cannot be just a docs issue.

    It is a doc issue compared to the current behavior: streams currently behave in this way, and it is expected for them to keep behaving in this manner as the commit you are referencing is 5 years old. You are proposing a change to this behavior which will be semver-major and I expect it to be a non-trivial change, considering possible breakages in userland.
    If you want to send a PR, please do!

  21. mpotra commented on Oct 19, 2017

    @mpotra
    Contributor

    @mcollina many thanks for clarifying why it's been a doc issue.
    However, I still believe that the current behavior should be reverted, since it breaks interoperability between the two modes of consuming a stream. I agree, this might indeed require a bit more work, and some more tests, and I don't expect it to land anytime soon.
    I'll send over a PR, once I get to properly refactor some bits, and hopefully that'll start a discussion about adjusting the behavior at least, in some future version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    docIssues and PRs related to Node.js documentation.streamIssues and PRs related to Node.js streams.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions