Why doesn't this program output any data? - node.js

I'm messing with the Node.js 0.10 Stream classes to try and figure out how to use them. I'm not sure why this experiment isn't working. It's supposed to output letters of the alphabet to an HTTP response object, but does not. I've annotated the source with a few comments.
Thank you!
var Readable = require('stream').Readable
, inherits = require('util').inherits
, http = require('http');
/**
* A stream that streams the English alphabet
*/
function AlphabetStream() {
Readable.call(this);
this.code = this.offset = 'a'.charCodeAt(0);
this.last = 'z'.charCodeAt(0);
}
inherits(AlphabetStream, Readable);
AlphabetStream.prototype._read = function(size) {
for (var i = 0; i < size; i++)
this.push(this.next_char());
this.push(null);
};
AlphabetStream.prototype.next_char = function() {
var cycle = this.last+1;
return String.fromCharCode((++this.code % cycle) + this.offset);
};
/**
* An HTTP server, prints the first n letters of the English alphabet
*/
var server = http.createServer(function(req, res) {
// $ curl localhost:3001/?size=11
var size = require('url').parse(req.url, true).query.size;
if (size) {
var rs = new AlphabetStream;
rs.pipe(res); // This calls `_read()` with `size` 16kb
rs.read(parseInt(size)); // This also calls `_read()` with `size` 16kb
}
res.end(''); // Nothing gets printed, despite the pipe and the reading.
});
server.listen(3001, function() {
console.log('Listening on 3001');
});

Have a look at this piece of code:
if (size) {
var rs = new AlphabetStream;
rs.pipe(res); // This calls `_read()` with `size` 16kb
rs.read(parseInt(size)); // This also calls `_read()` with `size` 16kb
}
res.end(''); // Nothing gets printed, despite the pipe and the reading.
You end the response (last line) before the actual piping can take place (this happens because .pipe is asynchronous). What you should do is something like that:
if (size) {
var rs = new AlphabetStream;
rs.pipe(res);
rs.read(parseInt(size));
} else {
// NOTE THE ELSE STATEMENT
res.end('');
}
.pipe function will take care of ending the destination stream (i.e. the response) unless explicitely stated otherwise, see the docs:
http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
EDIT As for why 16kb? Well, I had to do some tests and it seems that this is the default behaviour of .pipe (and I'm not sure how to change that to be honest). First of all note that this line:
rs.read(parseInt(size));
is totally useless (you can remove it). .pipe will take care of reading the data. Now the default behaviour is to read chunks of 16kb of data. So in order to do what you are trying to do you should probably pass size to the constructor of AlphabetStream, like this:
function AlphabetStream(size) {
Readable.call(this);
this.code = this.offset = 'a'.charCodeAt(0);
this.last = 'z'.charCodeAt(0);
this.size = size; // <--- store size here
}
inherits(AlphabetStream, Readable);
AlphabetStream.prototype._read = function(size) {
// this allows the stream to be a true stream
// it reads only as much data as it can
// but .read can be called multiple times without issues
// with a predefined limit
var chunk = Math.min(size, this.size);
this.size -= chunk;
for (var i = 0; i < chunk; i++) {
this.push(this.next_char());
}
if (!this.size) {
// end the stream only when the limit is reached
this.push(null);
}
};
after all a stream should not depend on how much data you read. Then you do:
if (size) {
var rs = new AlphabetStream(parseInt(size));
rs.pipe(res);
} else {
res.end('');
}

Related

How to write batches of data in NodeJS stream pipeline?

I have a function in which I read CSV file as a readable stream using the "pipeline" method, splitting it by rows and transforming the data of each row, then I add the data to an array. When the pipeline is finished, I insert all the data to a database.
This is the relevant part of the code:
pipeline(storageStream as Readable, split(), this.FilterPipe(), this.MapData(result));
public MapData(result: Array<string>): MapStream {
return mapSync((filteredData: string) => {
const trimmed: string = filteredData.trim();
if (trimmed.length !== 0) {
result.push(trimmed);
}
});
}
We have encountered sometimes with memory limits since we uploaded a big amount of very large CSV files, so we have decided to try to split the logic into insertion batches so we won't use a lot of memory at the same time.
So I thought to handle the readed data by batches, in which per every batch (let's say 100 rows in the file), I will trigger the "MapData" function and insert the result array to the DB.
Is there any option to add a condition so the MapData will be triggered every X rows?
Or, if there is any other solution that might meet the requirement?
Thanks in advance!
The following code shows a transform stream that buffers incoming objects (or arrays of objects) until it has 100 of them and then pushes them onwards as an array:
var t = new stream.Transform({
objectMode: true,
transform(chunk, encoding, callback) {
this.buffer = (this.buffer || []).concat(chunk);
if (this.buffer.length >= 100) {
this.push(this.buffer);
this.buffer = [];
}
callback();
},
flush(callback) {
if (this.buffer.length > 0) this.push(this.buffer);
callback();
}
}).on("data", console.log);
for (var i = 0; i < 250; i++) t.write(i);
t.end();
You can include such a transform stream in your pipeline.
And here's the same in Typescript. It can very probably be done more elegantly, but I am no Typescript expert.
class MyTransform extends Transform {
buffer: Array<any>;
}
var t = new MyTransform({
objectMode: true,
transform(chunk, encoding, callback) {
var that = this as MyTransform;
that.buffer = (that.buffer || []).concat(chunk);
if (that.buffer.length >= 100) {
this.push(that.buffer);
that.buffer = [];
}
callback();
},
flush(callback) {
var that = this as MyTransform;
if (that.buffer.length > 0) this.push(that.buffer);
callback();
}
}).on("data", console.log);
for (var i = 0; i < 250; i++) t.write(i);
t.end();

AudioBufferSourceNode not looping when duration is set

I've been playing around with js audio web api.
The thing I'm trying to achieve is to play a piece of a track in loop.
No problem playing the whole track in loop, but if I define a duration then it doesn't loop anymore...I guess what I need is like a marker more than a duration...if there a way to do this?
const audioCtx = new AudioContext();
const srcUrl = 'https://freesound.org/data/previews/251/251248_1137749-lq.mp3';
let srcArrayBuffer;
let playingTrack;
async function loadSrcAudioFile(url) {
const response = await fetch(url);
const buffer = await response.arrayBuffer();
return buffer;
}
function loop() {
playingTrack = audioCtx.createBufferSource();
playingTrack.connect(audioCtx.destination);
playingTrack.buffer = srcArrayBuffer;
playingTrack.loop = true;
//playingTrack.loopStart = 0;
//playingTrack.loopEnd = 1.5;
playingTrack.start(audioCtx.currentTime, 0, 1.5);
}
function stop() {
playingTrack.stop();
playingTrack = null;
}
async function play() {
if (!srcArrayBuffer) {
const buffer = await loadSrcAudioFile(srcUrl);
srcArrayBuffer = await audioCtx.decodeAudioData(buffer);
}
loop();
}
document.getElementById('playBtn').addEventListener('click', play);
document.getElementById('stopBtn').addEventListener('click', stop);
JSFIDDLE
You're almost there. Setting the duration means "play this for this long". It will stop after 1.5 seconds no matter what. It doesn't care if the buffer gets looped or not.
Setting loopStart and loopEnd without specifying the duration will do the trick.
https://jsfiddle.net/4yeavmp3/
If you for example want to loop it 4 times you could set the duration to 6.

node.js: create a connected writable and readable stream pair

I am trying to create a function that returns a connected writable and readable stream pair. eg:
const { writable, readable } = createStreamPair();
where each end has the right interface (writable instanceof stream.Readable === false and readable instanceof stream.Writable === false) unlike the PassThrough stream.
use case:
createWriteStream(filePath) {
const { writable, readable } = createStreamPair();
writeFile(filePath, readable);
return writable;
}
How to create my createStreamPair() function ?
Edit1
A naive approach that obviously does not work ...
function createStreamPair() {
var readable = new stream.Readable();
var writable = new stream.Writable();
readable.pipe(writable);
return { writable, readable }
}
The Node.js tests uses a function that creates two Duplex streams, writes to one can be read from the other, and vice-versa: https://github.com/nodejs/node/blob/master/test/common/duplexpair.js
It isn't part of the Node.js standard library, but you can write your own.
I'll present a slightly modified, annotated version here:
const Duplex = require('stream').Duplex;
const assert = require('assert');
// Define some unique property names.
// The actual value doesn't matter,
// so long as they're not used by Node.js for anything else.
const kCallback = Symbol('Callback');
const kOtherSide = Symbol('Other');
// Define a function `DuplexSocket` whose prototype inherits from `Duplex`
class DuplexSocket extends Duplex {
constructor() {
// Let Node.js initialize everything it needs to
super();
// Define two values we will be using
// kCallback saves a temporary reference to a function while
this[kCallback] = null;
// kOtherSide will be the reference to the other side of the stream
this[kOtherSide] = null;
}
_read() {
// This is called when this side receives a push() call
// If the other side set a callback for us to call,
// then first clear that reference
// (it might be immediately set to a new value again),
// then call the function.
const callback = this[kCallback];
if (callback) {
this[kCallback] = null;
callback();
}
}
_write(chunk, encoding, callback) {
// This is called when someone writes to the stream
// Ensure there's a reference to the other side before trying to call it
assert.notStrictEqual(this[kOtherSide], null);
// Ensure that the other-side callback is empty before setting it
// If push immediately calls _read, this should never be a problem
assert.strictEqual(this[kOtherSide][kCallback], null);
if (chunk.length === 0) {
// callback is not called for zero-length chunks
process.nextTick(callback);
} else {
// Let Node.js know when _read has been called
this[kOtherSide][kCallback] = callback;
// And finally, send the other side the data to be read
this[kOtherSide].push(chunk);
}
}
_final(callback) {
// Ask the other side to let us know it received our EOF request
this[kOtherSide].on('end', callback);
// And finally, pushing null signals the end of the stream
this[kOtherSide].push(null);
}
}
function makeDuplexPair() {
// Create two pairs of
const clientSide = new DuplexSocket();
const serverSide = new DuplexSocket();
// Set the other-side reference
clientSide[kOtherSide] = serverSide;
serverSide[kOtherSide] = clientSide;
// Both instances behave the same, so choice of name doesn't matter,
// So long as they're distinguishable.
return { clientSide, serverSide };
}
module.exports = makeDuplexPair;
Here's another way of creating two streams, one Readable and one Writable in this case:
function makeAsymmetricalStreamPair() {
var readableCallback;
const readableSide = new ReadableStream;
readableSide._read = function _read(){
if(!readableCallback) return;
var callback = readableCallback;
readableCallback = null;
callback();
}
const writableSide = new WritableStream;
writableSide._write = function _write(chunk, enc, callback){
if (readableCallback) throw new Error;
if (chunk.length === 0) {
process.nextTick(callback);
} else {
readableCallback = callback;
readableSide.push(chunk);
}
}
writableSide._final = function _final(callback){
readableSide.on('end', callback);
readableSide.push(null);
}
return { readableSide, writableSide };
}
As of today you can use stream.PassTrough

Should `drain` event be registered before the result of the write is available

I have this snippet of code:
const file = fs.createWriteStream('./test.txt');
let written = true;
// handler is added before even an attempt to write is made
file.on('drain', function () {
written = true;
console.log('drained');
});
const interval = setInterval(function () {
if (Date.now() - time > 10000) {
clearInterval(interval);
}
if (written) {
written = file.write(new Array(1000000).join('z'));
}
}, 100);
I'm wondering if that a standard practice to add handler even an attempt to write is made?
In case of using file.on('drain') listener you set up general listener to drain event of your stream.
Notice: This listener will be removed after closing of writable stream.
Generally that code will work proper, but most common practice in Node.js is to use stream.once('drain') handler for each case of internal buffer exceeding. That approach is covered in Node.js documentation for Event: 'drain':
function writeOneMillionTimes(writer, data, encoding, callback) {
var i = 1000000;
write();
function write() {
var ok = true;
do {
i -= 1;
if (i === 0) {
// last time!
writer.write(data, encoding, callback);
} else {
// see if we should continue, or wait
// don't pass the callback, because we're not done yet.
ok = writer.write(data, encoding);
}
} while (i > 0 && ok);
if (i > 0) {
// had to stop early!
// write some more once it drains
writer.once('drain', write);
}
}
}

How do you implement a stream that properly handles backpressure in node.js?

I can't for the life of me figure out how to implement a stream that properly handles backpressure. Should you never use pause and resume?
I have this implementation I'm trying to get to work correctly:
var StreamPeeker = exports.StreamPeeker = function(myStream, callback) {
stream.Readable.call(this, {highWaterMark: highWaterMark})
this.stream = myStream
myStream.on('readable', function() {
var data = myStream.read(5000)
//process.stdout.write("Eff: "+data)
if(data !== null) {
if(!this.push(data)) {
process.stdout.write("Pause")
this.pause()
}
callback(data)
}
}.bind(this))
myStream.on('end', function() {
this.push(null)
}.bind(this))
}
util.inherits(StreamPeeker, stream.Readable)
StreamPeeker.prototype._read = function() {
process.stdout.write("resume")
//this.resume() // putting this in for some reason causes the stream to not output???
}
It correctly sends output, but doesn't correctly produce backpressure. How can I change it to properly support backpressure?
Ok I finally figured it out after lots of trial and error. A couple guidelines:
Never ever use pause or resume (otherwise it'll go into legacy "flowing" mode)
Never add a "data" event listener (otherwise it'll go into legacy "flowing" mode)
Its the implementor's responsibility to keep track of when the source is readable
Its the implementor's responsibility to keep track of when the destination wants more data
The implementation should not read any data until the _read method is called
The argument to read tells the source to give it that many bytes, it probably best to pass the argument passed to this._read into the source's read method. This way you should be able to configure how much to read at a time at the destination, and the rest of the stream chain should be automatic.
So this is what I changed it to:
Update: I created a Readable that is much easier to implement with proper back-pressure, and should have just as much flexibility as node's native streams.
var Readable = stream.Readable
var util = require('util')
// an easier Readable stream interface to implement
// requires that subclasses:
// implement a _readSource function that
// * gets the same parameter as Readable._read (size)
// * should return either data to write, or null if the source doesn't have more data yet
// call 'sourceHasData(hasData)' when the source starts or stops having data available
// calls 'end()' when the source is out of data (forever)
var Stream666 = {}
Stream666.Readable = function() {
stream.Readable.apply(this, arguments)
if(this._readSource === undefined) {
throw new Error("You must define a _readSource function for an object implementing Stream666")
}
this._sourceHasData = false
this._destinationWantsData = false
this._size = undefined // can be set by _read
}
util.inherits(Stream666.Readable, stream.Readable)
Stream666.Readable.prototype._read = function(size) {
this._destinationWantsData = true
if(this._sourceHasData) {
pushSourceData(this, size)
} else {
this._size = size
}
}
Stream666.Readable.prototype.sourceHasData = function(_sourceHasData) {
this._sourceHasData = _sourceHasData
if(_sourceHasData && this._destinationWantsData) {
pushSourceData(this, this._size)
}
}
Stream666.Readable.prototype.end = function() {
this.push(null)
}
function pushSourceData(stream666Readable, size) {
var data = stream666Readable._readSource(size)
if(data !== null) {
if(!stream666Readable.push(data)) {
stream666Readable._destinationWantsData = false
}
} else {
stream666Readable._sourceHasData = false
}
}
// creates a stream that can view all the data in a stream and passes the data through
// correctly supports backpressure
// parameters:
// stream - the stream to peek at
// callback - called when there's data sent from the passed stream
var StreamPeeker = function(myStream, callback) {
Stream666.Readable.call(this)
this.stream = myStream
this.callback = callback
myStream.on('readable', function() {
this.sourceHasData(true)
}.bind(this))
myStream.on('end', function() {
this.end()
}.bind(this))
}
util.inherits(StreamPeeker, Stream666.Readable)
StreamPeeker.prototype._readSource = function(size) {
var data = this.stream.read(size)
if(data !== null) {
this.callback(data)
return data
} else {
this.sourceHasData(false)
return null
}
}
Old Answer:
// creates a stream that can view all the data in a stream and passes the data through
// correctly supports backpressure
// parameters:
// stream - the stream to peek at
// callback - called when there's data sent from the passed stream
var StreamPeeker = exports.StreamPeeker = function(myStream, callback) {
stream.Readable.call(this)
this.stream = myStream
this.callback = callback
this.reading = false
this.sourceIsReadable = false
myStream.on('readable', function() {
this.sourceIsReadable = true
this._readMoreData()
}.bind(this))
myStream.on('end', function() {
this.push(null)
}.bind(this))
}
util.inherits(StreamPeeker, stream.Readable)
StreamPeeker.prototype._read = function() {
this.reading = true
if(this.sourceIsReadable) {
this._readMoreData()
}
}
StreamPeeker.prototype._readMoreData = function() {
if(!this.reading) return;
var data = this.stream.read()
if(data !== null) {
if(!this.push(data)) {
this.reading = false
}
this.callback(data)
}
}

Resources