Skip to content

Commit a0a3c0e

Browse files
danmactoughclaude
andcommitted
Add async iterator support via Symbol.asyncIterator
FeedParser instances can now be consumed directly with for await...of. The implementation bridges the push-based stream event model to the pull-based async iteration protocol using an async generator, without changing the underlying readable-stream v2 implementation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 4606b49 commit a0a3c0e

4 files changed

Lines changed: 64 additions & 1 deletion

File tree

.eslintrc.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
{
22
"env": {
3-
"node": true
3+
"node": true,
4+
"es6": true
5+
},
6+
"parserOptions": {
7+
"ecmaVersion": 2018
48
},
59
"extends": "eslint:recommended",
610
"rules": {

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ declare class FeedParser extends stream.Transform {
1010
options: FeedParser.Options;
1111

1212
read(): FeedParser.Item | null;
13+
[Symbol.asyncIterator](): AsyncGenerator<FeedParser.Item>;
1314
resumeSaxError(): void;
1415

1516
on(event: 'meta', listener: (meta: FeedParser.Meta) => void): this;

lib/feedparser/index.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,4 +1237,45 @@ FeedParser.prototype._flush = function (done) {
12371237
* @typedef {import('readable-stream').Transform & FeedParserState} FeedParserInstance
12381238
*/
12391239

1240+
/** @this {FeedParserInstance} */
1241+
FeedParser.prototype[Symbol.asyncIterator] = async function* () {
1242+
var resolve = null;
1243+
var error = null;
1244+
var ended = false;
1245+
1246+
function onReadable() {
1247+
if (resolve) { resolve(); resolve = null; }
1248+
}
1249+
function onEnd() {
1250+
ended = true;
1251+
if (resolve) { resolve(); resolve = null; }
1252+
}
1253+
function onError(err) {
1254+
error = err;
1255+
if (resolve) { resolve(); resolve = null; }
1256+
}
1257+
1258+
this.on('readable', onReadable);
1259+
this.on('end', onEnd);
1260+
this.on('error', onError);
1261+
1262+
try {
1263+
while (true) {
1264+
var item;
1265+
while ((item = this.read()) !== null) {
1266+
yield item;
1267+
}
1268+
if (ended) break;
1269+
if (error) throw error;
1270+
await new Promise(function (r) { resolve = r; });
1271+
resolve = null;
1272+
if (error) throw error;
1273+
}
1274+
} finally {
1275+
this.removeListener('readable', onReadable);
1276+
this.removeListener('end', onEnd);
1277+
this.removeListener('error', onError);
1278+
}
1279+
};
1280+
12401281
exports = module.exports = FeedParser;

test/examples.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
var FeedParser = require('../');
2+
3+
describe('examples', function () {
4+
it('should work as an async iterator', async function () {
5+
var feedparser = new FeedParser();
6+
var feed = __dirname + '/feeds/rss2sample.xml';
7+
var items = [];
8+
9+
fs.createReadStream(feed).pipe(feedparser);
10+
11+
for await (const item of feedparser) {
12+
items.push(item);
13+
}
14+
15+
assert.equal(items.length, 4);
16+
});
17+
});

0 commit comments

Comments
 (0)