Browse Source

Add callbacks to stream methods

Allows for more fine graining, especially finding out about an individual
chunk of data being flushed in a write stream rather than the whole queue.

This commit also fixes a bug causing forceClose to fail on a readStream that
did not finish opening yet.
v0.7.4-release
Felix Geisendörfer 15 years ago
committed by Ryan Dahl
parent
commit
9a9f08b1bc
  1. 11
      doc/api.txt
  2. 75
      lib/fs.js
  3. 9
      test/simple/test-file-read-stream.js
  4. 20
      test/simple/test-file-write-stream.js

11
doc/api.txt

@ -823,7 +823,7 @@ until the stream is resumed.
+readStream.resume()+ :: +readStream.resume()+ ::
Resumes the stream. Together with +pause()+ this useful to throttle reading. Resumes the stream. Together with +pause()+ this useful to throttle reading.
+readStream.forceClose()+ :: +readStream.forceClose([callback])+ ::
Allows to close the stream before the +"end"+ is reached. No more events other Allows to close the stream before the +"end"+ is reached. No more events other
than +"close"+ will be fired after this method has been called. than +"close"+ will be fired after this method has been called.
@ -855,15 +855,18 @@ Returns a new FileWriteStream object.
A boolean that is +true+ by default, but turns +false+ after an +"error"+ A boolean that is +true+ by default, but turns +false+ after an +"error"+
occured or +close()+ / +forceClose()+ was called. occured or +close()+ / +forceClose()+ was called.
+writeStream.write(data)+ :: +writeStream.write(data, [callback])+ ::
Returns +true+ if the data was flushed to the kernel, and +false+ if it was Returns +true+ if the data was flushed to the kernel, and +false+ if it was
queued up for being written later. A +"drain"+ will fire after all queued data queued up for being written later. A +"drain"+ will fire after all queued data
has been written. has been written.
+
You can also specify +callback+ to be notified when the data from this write
has been flushed. The first param is +err+, the second is +bytesWritten+.
+writeStream.close()+ :: +writeStream.close([callback])+ ::
Closes the stream right after all queued +write()+ calls have finished. Closes the stream right after all queued +write()+ calls have finished.
+writeStream.forceClose()+ :: +writeStream.forceClose([callback])+ ::
Allows to close the stream regardless of its current state. Allows to close the stream regardless of its current state.
== HTTP == HTTP

75
lib/fs.js

@ -495,16 +495,31 @@ var FileReadStream = exports.FileReadStream = function(path, options) {
read(); read();
}); });
this.forceClose = function() { this.forceClose = function(cb) {
this.readable = false; this.readable = false;
fs.close(this.fd, function(err) {
if (err) {
self.emit('error', err);
return;
}
self.emit('close'); function close() {
}); fs.close(self.fd, function(err) {
if (err) {
if (cb) {
cb(err);
}
self.emit('error', err);
return;
}
if (cb) {
cb(null);
}
self.emit('close');
});
}
if (this.fd) {
close();
} else {
this.addListener('open', close);
}
}; };
this.pause = function() { this.pause = function() {
@ -546,7 +561,7 @@ var FileWriteStream = exports.FileWriteStream = function(path, options) {
queue = [], queue = [],
busy = false; busy = false;
queue.push([fs.open, this.path, this.flags, this.mode]); queue.push([fs.open, this.path, this.flags, this.mode, undefined]);
function flush() { function flush() {
if (busy) { if (busy) {
@ -560,29 +575,40 @@ var FileWriteStream = exports.FileWriteStream = function(path, options) {
busy = true; busy = true;
var method = args.shift(); var
method = args.shift(),
cb = args.pop();
args.push(function(err) { args.push(function(err) {
busy = false; busy = false;
if (err) { if (err) {
self.writeable = false; self.writeable = false;
if (cb) {
cb(err);
}
self.emit('error', err); self.emit('error', err);
return; return;
} }
// save reference for file pointer
if (method === fs.open) {
self.fd = arguments[1];
self.emit('open', self.fd);
}
// stop flushing after close // stop flushing after close
if (method === fs.close) { if (method === fs.close) {
if (cb) {
cb(null);
}
self.emit('close'); self.emit('close');
return; return;
} }
// save reference for file pointer
if (method === fs.open) {
self.fd = arguments[1];
self.emit('open', self.fd);
} else if (cb) {
// write callback
cb(null, arguments[1]);
}
flush(); flush();
}); });
@ -594,30 +620,37 @@ var FileWriteStream = exports.FileWriteStream = function(path, options) {
method.apply(null, args); method.apply(null, args);
}; };
this.write = function(data) { this.write = function(data, cb) {
if (!this.writeable) { if (!this.writeable) {
throw new Error('stream not writeable'); throw new Error('stream not writeable');
} }
queue.push([fs.write, data, undefined, this.encoding]); queue.push([fs.write, data, undefined, this.encoding, cb]);
flush(); flush();
return false; return false;
}; };
this.close = function() { this.close = function(cb) {
this.writeable = false; this.writeable = false;
queue.push([fs.close,]); queue.push([fs.close, cb]);
flush(); flush();
}; };
this.forceClose = function() { this.forceClose = function(cb) {
this.writeable = false; this.writeable = false;
fs.close(self.fd, function(err) { fs.close(self.fd, function(err) {
if (err) { if (err) {
if (cb) {
cb(err);
}
self.emit('error', err); self.emit('error', err);
return; return;
} }
if (cb) {
cb(null);
}
self.emit('close'); self.emit('close');
}); });
}; };

9
test/simple/test-file-read-stream.js

@ -7,7 +7,8 @@ var
callbacks = { callbacks = {
open: -1, open: -1,
end: -1, end: -1,
close: -1 close: -1,
forceClose: -1
}, },
paused = false, paused = false,
@ -47,6 +48,12 @@ file
assert.equal(fs.readFileSync(fn), fileContent); assert.equal(fs.readFileSync(fn), fileContent);
}); });
var file2 = fs.createReadStream(fn);
file2.forceClose(function(err) {
assert.ok(!err);
callbacks.forceClose++;
});
process.addListener('exit', function() { process.addListener('exit', function() {
for (var k in callbacks) { for (var k in callbacks) {
assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]); assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]);

20
test/simple/test-file-write-stream.js

@ -4,12 +4,14 @@ var
fn = path.join(fixturesDir, "write.txt"), fn = path.join(fixturesDir, "write.txt"),
file = fs.createWriteStream(fn), file = fs.createWriteStream(fn),
EXPECTED = '0123456789', EXPECTED = '012345678910',
callbacks = { callbacks = {
open: -1, open: -1,
drain: -2, drain: -2,
close: -1 close: -1,
closeCb: -1,
write: -11,
}; };
file file
@ -27,7 +29,10 @@ file
file.write(EXPECTED); file.write(EXPECTED);
} else if (callbacks.drain == 0) { } else if (callbacks.drain == 0) {
assert.equal(EXPECTED+EXPECTED, fs.readFileSync(fn)); assert.equal(EXPECTED+EXPECTED, fs.readFileSync(fn));
file.close(); file.close(function(err) {
assert.ok(!err);
callbacks.closeCb++;
});
} }
}) })
.addListener('close', function() { .addListener('close', function() {
@ -39,8 +44,13 @@ file
fs.unlinkSync(fn); fs.unlinkSync(fn);
}); });
for (var i = 0; i < 10; i++) { for (var i = 0; i < 11; i++) {
assert.strictEqual(false, file.write(i)); (function(i) {
assert.strictEqual(false, file.write(i, function(err, bytesWritten) {
callbacks.write++;
assert.equal(new String(i).length, bytesWritten);
}));
})(i);
} }
process.addListener('exit', function() { process.addListener('exit', function() {

Loading…
Cancel
Save