Skip to content

Instantly share code, notes, and snippets.

@jacquarg
Created December 10, 2014 11:33
Show Gist options
  • Save jacquarg/e3353679a954ccec7c24 to your computer and use it in GitHub Desktop.
Save jacquarg/e3353679a954ccec7c24 to your computer and use it in GitHub Desktop.
ReadLineStream.eachSeries(stream, iterator, callback)
# Read stream line by line tool.
class ReadLineStream
# Applies the function iterator to each line in characters stream, in
# series. The next iterator is only called once the current one has
# completed. This means the iterator functions will complete in the order
# of the lines.
# API mimics async.eachSeries.
# There isn't error handling yet.
eachSeries: (stream, iterator, callback) ->
@stream = stream
@iteratorCallback = iterator
@endCallback = callback
# Synchronization flags
# stream.read mutex
@isReadingStream = false
# End of file flag.
@eof = false
@lines = []
# Used to merge cutted line on successive stream.read boundaries.
@lastLine = "" # Initialize with empty string, to merge with start.
@stream.on 'end', => @eof = true
@stream.on 'readable', @onReadable
onReadable: =>
if not @isReadingStream
@read()
# else
# Is there blocking scenarios ?
# Blocking if read not called after onReadable,
# Needs @isReadingStream is true while onReadable run.
# onReadable can occurs only in getLine() execution
# so, before the next stream.read call.
#
# NO blocking scenario.
read: =>
@isReadingStream = true
raw = @stream.read()
if raw isnt null
rawLines = raw.toString().split '\n'
# Merge the line cutted by stream.read boundary.
rawLines[0] = @lastLine + rawLines[0]
@lastLine = rawLines.pop()
@lines = rawLines
@getLine()
else if @eof
@iteratorCallback @lastLine, @endCallback
else
@isReadingStream = false #wait next 'readable' event.
getLine: =>
# Recursive deferral.
setTimeout @_getLine, 0
_getLine: =>
if @lines.length
line = @lines.shift()
@iteratorCallback line, @getLine
else
@read()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment