├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── package.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - '0.12' 5 | - 'iojs' 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Mathias Buus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stream-iterate 2 | 3 | Iterate through the values in a stream. 4 | 5 | ``` 6 | npm install stream-iterate 7 | ``` 8 | 9 | [![build status](http://img.shields.io/travis/mafintosh/stream-iterate.svg?style=flat)](http://travis-ci.org/mafintosh/stream-iterate) 10 | 11 | ## Usage 12 | 13 | ``` js 14 | var iterate = require('stream-iterate') 15 | var from = require('from2') 16 | 17 | var stream = from.obj(['a', 'b', 'c']) 18 | 19 | var read = iterate(stream) 20 | 21 | loop() 22 | 23 | // recursively iterates through each item in the stream 24 | function loop () { 25 | read(function (err, data, next) { 26 | console.log(err, data) 27 | next() 28 | loop() 29 | }) 30 | } 31 | ``` 32 | 33 | If you don't call `next` and call `read` again the same `(err, value)` pair will be returned. 34 | 35 | You can use this module to implement stuff like [a streaming merge sort](https://github.com/mafintosh/stream-iterate/blob/master/test.js#L5-L47). 36 | 37 | ## License 38 | 39 | [MIT](LICENSE) 40 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var Readable = require('readable-stream').Readable 2 | var shift = require('stream-shift') 3 | 4 | var stream2 = function (stream) { 5 | if (stream._readableState) return stream 6 | return new Readable({objectMode: true, highWaterMark: 16}).wrap(stream) 7 | } 8 | 9 | module.exports = function (stream) { 10 | stream = stream2(stream) 11 | 12 | var ended = false 13 | var data = null 14 | var err = null 15 | var destroyed = false 16 | var fn = null 17 | 18 | var consume = function (e) { 19 | if (e) { 20 | destroyed = true 21 | if (stream.destroy) stream.destroy(e) 22 | return 23 | } 24 | 25 | data = null 26 | err = null 27 | } 28 | 29 | var onresult = function () { 30 | if (!fn) return 31 | var tmp = fn 32 | fn = undefined 33 | tmp(err, data, consume) 34 | } 35 | 36 | var update = function () { 37 | if (!fn) return 38 | data = shift(stream) 39 | if (data === null && !ended) return 40 | onresult() 41 | } 42 | 43 | var onend = function () { 44 | ended = true 45 | onresult() 46 | } 47 | 48 | stream.on('readable', update) 49 | 50 | stream.on('error', function (e) { 51 | err = e 52 | onresult() 53 | }) 54 | 55 | stream.on('close', function () { 56 | if (stream._readableState.ended) return 57 | onend() 58 | }) 59 | 60 | stream.on('end', onend) 61 | 62 | return function (callback) { 63 | if (destroyed) return 64 | if (err) return callback(err, null, consume) 65 | if (data) return callback(null, data, consume) 66 | if (ended) return callback(null, null, consume) 67 | fn = callback 68 | update() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stream-iterate", 3 | "version": "1.2.0", 4 | "description": "Iterate through the values of a stream", 5 | "main": "index.js", 6 | "dependencies": { 7 | "readable-stream": "^2.1.5", 8 | "stream-shift": "^1.0.0" 9 | }, 10 | "devDependencies": { 11 | "from2": "^1.3.0", 12 | "standard": "^3.3.2", 13 | "tape": "^4.0.0" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/mafintosh/stream-iterate.git" 18 | }, 19 | "scripts": { 20 | "test": "standard && tape test.js" 21 | }, 22 | "author": "Mathias Buus (@mafintosh)", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/mafintosh/stream-iterate/issues" 26 | }, 27 | "homepage": "https://github.com/mafintosh/stream-iterate" 28 | } 29 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var tape = require('tape') 2 | var from = require('from2') 3 | var iterate = require('./') 4 | 5 | tape('merge sort', function (t) { 6 | var a = from.obj(['a', 'b', 'd', 'e', 'g', 'h']) 7 | var b = from.obj(['b', 'c', 'f']) 8 | var output = [] 9 | 10 | var readA = iterate(a) 11 | var readB = iterate(b) 12 | 13 | var loop = function () { 14 | readA(function (err, dataA, nextA) { 15 | if (err) throw err 16 | readB(function (err, dataB, nextB) { 17 | if (err) throw err 18 | 19 | if (!dataA && !dataB) { 20 | t.same(output, ['a', 'b', 'b', 'c', 'd', 'e', 'f', 'g', 'h'], 'sorts list') 21 | t.end() 22 | return 23 | } 24 | 25 | if (!dataB || dataA < dataB) { 26 | output.push(dataA) 27 | nextA() 28 | return loop() 29 | } 30 | 31 | if (!dataA || dataA > dataB) { 32 | output.push(dataB) 33 | nextB() 34 | return loop() 35 | } 36 | 37 | output.push(dataA) 38 | output.push(dataB) 39 | nextA() 40 | nextB() 41 | loop() 42 | }) 43 | }) 44 | } 45 | 46 | loop() 47 | }) 48 | 49 | tape('error handling', function (t) { 50 | var a = from.obj(['a', 'b', 'd', 'e', 'g', 'h']) 51 | var read = iterate(a) 52 | 53 | a.destroy(new Error('oh no')) 54 | 55 | read(function (err) { 56 | t.ok(err, 'had error') 57 | t.same(err.message, 'oh no') 58 | t.end() 59 | }) 60 | }) 61 | --------------------------------------------------------------------------------