Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement listener subscription as async iterator protocol #20

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5656357
feat: register event listener with asynchronous iterator protocol - c…
lorenzofox3 Jan 2, 2018
78cbcd6
keep single line block formatting
lorenzofox3 Jan 2, 2018
b50ec91
fix xo and test setting
lorenzofox3 Jan 3, 2018
c904739
enable xo
lorenzofox3 Jan 3, 2018
c951511
removed useless eslint flag comments
lorenzofox3 Jan 3, 2018
d9b617a
split async iterator behavior to different file
lorenzofox3 Jan 3, 2018
cac49ab
Revert "split async iterator behavior to different file"
novemberborn Jan 4, 2018
ade1383
Merge branch 'master' into pr/20
novemberborn Jan 4, 2018
a4d414d
Implement async iterator protocol without generators, fix tests
novemberborn Jan 4, 2018
a6a3476
Fix linting error
novemberborn Jan 6, 2018
e91559c
Simplify next() implementation
novemberborn Jan 6, 2018
c4ca867
Handle edge case where return() is called by an earlier listener for …
novemberborn Jan 6, 2018
e18c260
Implement return() according to spec
novemberborn Jan 6, 2018
edb7ac5
Remove unnecessary tsconfig files in test fixtures
novemberborn Jan 6, 2018
756b47a
Change how TypeScript is loaded in types test
novemberborn Jan 6, 2018
7ef2983
Return async iterator from .events(), not .on()
novemberborn Jan 6, 2018
463c4e6
Implement .anyEvent()
novemberborn Jan 6, 2018
3f9aa37
Tweak AVA's Babel options
novemberborn Jan 6, 2018
ef596e6
Support running tests without for-await-of transpilation
novemberborn Jan 6, 2018
361cddd
Fix for-await transpilation
novemberborn Jan 7, 2018
735ee10
Ensure async iterators return non-promise values
novemberborn Jan 7, 2018
6d73663
Merge branch 'master' into pr/20
novemberborn Jan 20, 2018
0afc521
Tweak iterator implementation now that scheduling is more consistent
novemberborn Jan 20, 2018
4cdcd6f
Remove wrongly placed documentation
novemberborn Jan 20, 2018
94f54c0
Separate iterator production
novemberborn Jan 20, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 31 additions & 15 deletions index.js
Expand Up @@ -8,27 +8,43 @@ function assertEventName(eventName) {
}
}

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

try {
/* eslint-disable no-constant-condition */
/* eslint-disable no-await-in-loop */
while (true) {
return {
async next() {
if (!queue) {
return {done: true};
}

if (queue.length > 0) {
yield queue.shift();
} else {
yield await emitter.once(eventName);
return {done: false, value: queue.shift()};
}

const value = await new Promise(resolve => {
flush = data => {
resolve(data);
flush = null;
};
});
return {done: false, value};
},
return() {
off();
queue = null;
Copy link
Contributor

@dinoboff dinoboff Jan 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be async too, take a value argument and return {done: true, value}.

https://tc39.github.io/proposal-async-iteration/#sec-asynciterator-interface

},
[Symbol.asyncIterator]() {
return this;
}
/* eslint-enable no-constant-condition */
/* eslint-enable no-await-in-loop */
} finally {
off();
}
};
}

class Emittery {
Expand Down
25 changes: 14 additions & 11 deletions package.json
Expand Up @@ -66,28 +66,31 @@
"typescript": "^2.6.2",
"xo": "*"
},
"xo": {
"parser": "babel-eslint"
"ava": {
"babel": {
"plugins": [
"transform-async-generator-functions"
],
"presets": [
"@ava/stage-4",
"@ava/transform-test-files"
]
}
},
"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",
"lcov",
"text"
]
},
"xo": {
"parser": "babel-eslint"
}
}
53 changes: 35 additions & 18 deletions test/_run.js
@@ -1,6 +1,24 @@
import test from 'ava';
import delay from 'delay';

// babel-plugin-transform-async-generator-functions assumes
// `Symbol.asyncIterator` exists, so stub it for iterator tests.
function stubAsyncIteratorSymbol(next) {
return async (...args) => {
if (!Symbol.asyncIterator) {
Symbol.asyncIterator = Symbol.for('Emittery.asyncIterator');
}

try {
return await next(...args);
} finally {
if (Symbol.asyncIterator === Symbol.for('Emittery.asyncIterator')) {
delete Symbol.asyncIterator;
}
}
};
}

module.exports = Emittery => {
test('on()', t => {
const emitter = new Emittery();
Expand Down Expand Up @@ -37,27 +55,26 @@ module.exports = Emittery => {
t.is(emitter._events.get('🦄').size, 1);
});

test('on() - async iterator', async t => {
const fixture = '🌈';
test.serial('on() - async iterator', stubAsyncIteratorSymbol(async t => {
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);
});
await emitter.emit('🦄', '🌈');
setTimeout(() => {
emitter.emit('🦄', '🌟');
}, 10);

t.plan(3);
const expected = ['🌈', '🌟'];
for await (const data of iterator) {
t.deepEqual(data, expected.shift());
if (expected.length === 0) {
break;
}
}

t.deepEqual(await iterator.next(), {done: true});
}));

test('off()', t => {
const emitter = new Emittery();
Expand Down