Skip to content

Commit

Permalink
split async iterator behavior to different file
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzofox3 committed Jan 3, 2018
1 parent c951511 commit d9b617a
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 69 deletions.
39 changes: 3 additions & 36 deletions index.js
@@ -1,36 +1,8 @@
'use strict';
const assertEventName = require('./util');

const resolvedPromise = Promise.resolve();

function assertEventName(eventName) {
if (typeof eventName !== 'string') {
throw new TypeError('eventName must be a string');
}
}

async function * iterator(emitter, eventName) {
const queue = [];
const off = emitter.on(eventName, data => {
queue.push(data);
});

try {
/* eslint-disable no-constant-condition */
/* eslint-disable no-await-in-loop */
while (true) {
if (queue.length > 0) {
yield queue.shift();
} else {
yield await emitter.once(eventName);
}
}
/* eslint-enable no-constant-condition */
/* eslint-enable no-await-in-loop */
} finally {
off();
}
}

module.exports = class Emittery {
constructor() {
this._events = new Map();
Expand All @@ -47,13 +19,8 @@ module.exports = class Emittery {

on(eventName, listener) {
assertEventName(eventName);

if (typeof listener === 'function') {
this._getListeners(eventName).add(listener);
return this.off.bind(this, eventName, listener);
}

return iterator(this, eventName);
this._getListeners(eventName).add(listener);
return this.off.bind(this, eventName, listener);
}

off(eventName, listener) {
Expand Down
37 changes: 37 additions & 0 deletions iterator.js
@@ -0,0 +1,37 @@
'use strict';
const assertEventName = require('./util');
const EmitteryClass = require('./index');

async function * iterator(emitter, eventName) {
const queue = [];
const off = emitter.on(eventName, data => {
queue.push(data);
});

try {
/* eslint-disable no-constant-condition */
/* eslint-disable no-await-in-loop */
while (true) {
if (queue.length > 0) {
yield queue.shift();
} else {
await emitter.once(eventName);
}
}
/* eslint-enable no-constant-condition */
/* eslint-enable no-await-in-loop */
} finally {
off();
}
}

module.exports = class IterableEmittery extends EmitteryClass {
on(eventName, listener) {
assertEventName(eventName);
if (typeof listener === 'function') {
this._getListeners(eventName).add(listener);
return this.off.bind(this, eventName, listener);
}
return iterator(this, eventName);
}
};
14 changes: 3 additions & 11 deletions package.json
Expand Up @@ -16,7 +16,8 @@
"build": "babel --out-file=legacy.js index.js",
"build:watch": "npm run build -- --watch",
"prepublish": "npm run build",
"test": "xo && nyc ava"
"test:next": "xo && node --harmony ./node_modules/.bin/ava",
"test": "xo && nyc ava ./test/main.js ./test/legacy.js"
},
"files": [
"index.js",
Expand Down Expand Up @@ -51,7 +52,6 @@
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-eslint": "^8.1.2",
"babel-plugin-transform-async-generator-functions": "^6.24.1",
"babel-plugin-transform-async-to-generator": "^6.24.1",
"babel-plugin-transform-es2015-spread": "^6.22.0",
"codecov": "^3.0.0",
Expand All @@ -65,17 +65,9 @@
"babel": {
"plugins": [
"transform-async-to-generator",
"transform-es2015-spread",
"transform-async-generator-functions"
],
"presets":[
"@ava/stage-4"
"transform-es2015-spread"
]
},
"ava": {
"require": "babel-register",
"babel": "inherit"
},
"nyc": {
"reporter": [
"html",
Expand Down
6 changes: 6 additions & 0 deletions readme.md
Expand Up @@ -34,6 +34,10 @@ emitter.emit('πŸ¦„', '🌈');

The above only works in Node.js 8 or newer. For older Node.js versions you can use `require('emittery/legacy')`.

### Node.js 9+

If you want the benefits of async iterators syntax your can use emittery from `require('emittery/iterator')`. Note you'll need to pass the relevant harmony flag to your nodejs process.
[see API#on for more details](#oneventname-listener)

## API

Expand All @@ -47,6 +51,8 @@ Returns an unsubscribe method.

Using the same listener multiple times for the same event will result in only one method call per emitted event.

##### Async iterator syntax

If you use the method with only the first argument, it will return an asynchronous iterator. Your listener will therefore be the loop body and you'll be able to
unsubscribe to the event simply by breaking the loop.

Expand Down
22 changes: 0 additions & 22 deletions test/_run.js
Expand Up @@ -37,28 +37,6 @@ module.exports = Emittery => {
t.is(emitter._events.get('πŸ¦„').size, 1);
});

test('on() - async iterator', async t => {
const fixture = '🌈';
const emitter = new Emittery();
setTimeout(() => {
emitter.emit('πŸ¦„', fixture);
}, 300);
const iterator = emitter.on('πŸ¦„');
const {value, done} = await iterator.next();
t.deepEqual(done, false);
t.deepEqual(value, fixture);
});

test('on() - async iterator (queued)', async t => {
const fixture = '🌈';
const emitter = new Emittery();
const iterator = emitter.on('πŸ¦„');
emitter.emit('πŸ¦„', fixture);
const {value, done} = await iterator.next();
t.deepEqual(done, false);
t.deepEqual(value, fixture);
});

test('off()', t => {
const emitter = new Emittery();
const listener = () => {};
Expand Down
43 changes: 43 additions & 0 deletions test/iterator.js
@@ -0,0 +1,43 @@
import test from 'ava';

let Emittery;
try {
Emittery = require('../iterator');
} catch (err) {
test('does not work due to syntax errors', t => {
t.is(err.name, 'SyntaxError');
});
}

if (Emittery) {
require('./_run')(Emittery);
/* eslint-disable ava/no-async-fn-without-await */
test('on() - async iterator (for await)', async t => {
t.plan(3);
const fixture = '🌈';
const emitter = new Emittery();
setInterval(() => {
emitter.emit('πŸ¦„', fixture);
}, 50);
let count = 0;
for await (const data of emitter.on('πŸ¦„')) {
count++;
if (count >= 3) {
break;
}
t.deepEqual(data, fixture);
}
});
/* eslint-enable ava/no-async-fn-without-await */

test('on() - async iterator', async t => {
const fixture = '🌈';
const emitter = new Emittery();
const iterator = emitter.on('πŸ¦„');
emitter.emit('πŸ¦„', fixture);
const {value, done} = await iterator.next();
t.deepEqual(done, false);
t.deepEqual(value, fixture);
});
}

6 changes: 6 additions & 0 deletions util.js
@@ -0,0 +1,6 @@
'use strict';
module.exports = function (eventName) {
if (typeof eventName !== 'string') {
throw new TypeError('eventName must be a string');
}
};

0 comments on commit d9b617a

Please sign in to comment.