-
Notifications
You must be signed in to change notification settings - Fork 147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ensure _.through Propogates Node Stream Errors #240
Conversation
Does your proposed fix work? It's still using |
I don't have time to dig back into this at the moment, but it's definitely something we want to do; relevant PR: #166 |
See my comment in #136 about this. |
Actually, you're right my fix won't work, is there even a need for the second pipe if we just wrap the whole thing in a Highland stream? Stream.prototype.through = function (target) {
if (_.isFunction(target)) {
return target(this);
}
else {
target.pause();
return _(this.pipe(target));
}
}; |
Hmm, good point. This would wrap errors that For a proper fix, I think we need to be more careful about laziness. As it is, the source stream will be completely drained (and buffered downstream) as soon as the |
Actually, I take that back about the laziness. The readable stream constructor uses |
And an error in |
So I tried a naive implementation just based on Stream.prototype.through = function (target) {
var self = this,
dest = _(target),
wrapper;
if (_.isFunction(target)) {
return target(this);
}
else {
wrapper = _(function (push, next) {
self.pull(function (err, x) {
if (err) {
wrapper._send(err);
next();
}
else if (x === nil) {
wrapper._send(null, nil);
}
else {
wrapper._send(null, x);
next();
}
});
});
wrapper.write = function (x) {
dest.write(x);
};
return wrapper;
}
}; This catches any errors from _(fs.createReadStream('test.json')).through(JSONStream.parse())
.stopOnError(_.log)
.toArray(_.log);
//=> [ <Buffer 7b 7a 22 61 22 3a 31 2c 22 62 22 3a 32 7d 0a> ]
// should give error because the JSON is invalid I also tried using if (_.isFunction(target)) {
return target(this);
}
else {
//resuming/handling drain etc left out for clarity
var s = self.consume(function (err, x, push, next) {
if(err) {
push(err);
}
else if (x === nil) {
dest.end();
}
}
else if (dest.write(x) !== false) {
next();
}
});
}
return s;
_(fs.createReadStream('test.json')).through2(JSONStream.parse())
.stopOnError(_.log)
.toArray(_.log);
//=> [ <Buffer 7b 7a 22 61 22 3a 31 2c 22 62 22 3a 32 7d 0a> ]
// should give error because the JSON is invalid Tbh, I'm not really sure what I'm doing here so I could be missing something really obvious. |
The first one doesn't work because the stream constructor treats node The second one is probably closer, but without the rest of that code, I What about using |
Yeah, that's much simpler and works now for all cases. else {
target.pause();
var s = _(this.on('error', function(err){
s._send(err);
}).pipe(target));
return s;
} Just for my own information, why do we pause the target stream there? I know it's important because the tests fail if we don't but I'd like understand why. |
Actually, this won't work for all cases because if the first item in the stream is an |
Yeah, that's what I meant by "you have to inline the constructor". If you You'll need to do the same. Create the stream, bind the two error handlers,
|
Ah right, I'd no idea what you meant by node stream branch until I realised you were talking about the 3.0 code base. Binding the two error handler works well: else {
target.pause();
var s = new Stream();
this.on('error', function (xs, err) {
s.write(new StreamError(err));
});
target.on('error', function (xs, err) {
s.write(new StreamError(err));
});
return this.pipe(target).pipe(s);
} It passes for my JSONStream code and with a generator function that creates errors but I'm having trouble with the var parser = through(
function (data) {
this.queue(JSON.parse(data));
},
function () {
this.queue(null);
}
);
var s = _.through(parser, ['zz{"a": 1}']);
s.errors(function (err) {
console.log(err);
}).toArray(function (xs) {
console.log(xs);
}); I can't understand why |
I think there's some equivalent code on 2.x too, some where in the Does
|
Of course, I should be doing something like this in the creation of the var parser = through(
function (data) {
try {
this.queue(JSON.parse(data));
}
catch (err) {
this.emit('error', err);
}
},
function () {
this.queue(null);
}
); Like yourself, I'm not familiar with |
*/ | ||
|
||
Stream.prototype.through = function (target) { | ||
var s, writeErr = _.curry(function (xs, err) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to do this convoluted curry thing. You already have a closure with the variable s
. Just define a
function writeErr(err) {
s.write(new StreamError(err));
}
95f4220
to
24d2756
Compare
All done. |
Also, can you change the commit message to something that describes the the commit does now? It no longer just adds a note. |
Do you think we should check if |
I actually tried to change to commit message a couple of times but for some reason it wouldn't work. I think it's possible to change the message when merging the pull request so maybe we can do that? Not sure if there's a huge benefit in doing the check for the second pipe. Maybe if it becomes a bottleneck in the future... |
Gotcha about the check. You can't do It's possible to change the message for the merge commit that's created, but the message for the original commit is still there. |
add note for _.through docs on node stream errors add error handlers to both pipe functions in through and tidy up tests fix typo fix docs and amend writeErr function use more descriptive name for output stream add note for _.through docs on node stream errors add error handlers to both pipe functions in through and tidy up tests fix typo fix docs and amend writeErr function use more descriptive name for output stream
ee78ad1
to
548b808
Compare
Yeah, I tried |
Hmm dunno what you did, but it seems to have fixed it. |
Ensure _.through propogates node stream errors. Resolves #136.
I've recently been using Dominic Tarr's JSONStream and found it very puzzling why errors in
_.through
were not being caught by myerrors
function:The reason I used
through
was because I wanted to 'Highlandify' the only non-Highland stream in my chain and I suspect that's what many others would think too. After a bit of digging around in the code and the issues I found that there's a fix for this planned but it's part of the wider refactorings planned for 3.0 (#191). I'm just adding a note to the documentation to warn people about this behaviour in case they get tripped up by it like I did. There's a fairly simple work around, which is to wrap the wholepipe
expression in another Highland stream:But it strikes me, would it not be an acceptable interim fix to just do this in the
through
method rather than wait until 3.0 drops?Or is there something I'm not considering?