From 67eee6e0e390dfe6f186b8258619609bc16a2b85 Mon Sep 17 00:00:00 2001 From: Victor Vu Date: Sat, 6 Jul 2019 22:00:51 -0500 Subject: [PATCH] Add support for async iterables. This is now possible thanks to the work in #682. --- CHANGELOG.md | 6 +++++- lib/index.js | 35 ++++++++++++++++++++++++++++++----- test/test.js | 10 ++++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4effa2b..4ef1c1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,11 @@ this library. ### New additions * `takeWhile` - returns a new stream that ends when the function passed as a parameter stops returning true. - [#677](https://github.com/caolan/highland/pull/677) + [#677](https://github.com/caolan/highland/pull/677). +* Async Iterator/Iterable support - Taks an object that is a async iterator or + iterable as defined by the [Async Iteration + specs](https://tc39.es/proposal-async-iteration/). + [#682](https://github.com/caolan/highland/pull/682). 3.0.0-beta.9 ----- diff --git a/lib/index.js b/lib/index.js index a26d86b..d64b8f5 100755 --- a/lib/index.js +++ b/lib/index.js @@ -131,6 +131,12 @@ function bindContext(fn, context) { * iterator's done value returns true. If the iterator's `next()` method throws or rejects, the exception will be emitted as an error, * and the stream will be ended with no further calls to `next()`. * + * **Asynchronous Iterable -** Accepts an object with a `Symbol.asyncIterator` + * property that conforms to the [async iteration + * spec](https://github.com/tc39/proposal-async-iteration#async-iterators-and-async-iterables). + * The constructor will create a async iterator and use it to generator emitted + * values. + * * @id _(source) * @section Stream Objects * @name _(source) @@ -199,15 +205,30 @@ function bindContext(fn, context) { * // from a Promise object * var foo = _($.getJSON('/api/foo')); * - * //from an iterator + * // from an iterator * var map = new Map([['a', 1], ['b', 2]]); * var bar = _(map.values()).toArray(_.log); * //=> [1, 2] * - * //from an iterable + * // from an iterable * var set = new Set([1, 2, 2, 3, 4]); * var bar = _(set).toArray(_.log); - * //=> [ 1, 2, 3, 4] + * //=> [1, 2, 3, 4] + * + * // from an async iterator + * async function* generator() { + * yield 1; + * yield 2; + * } + * var stream = _(generator()).toArray(_.log); + * //=> [1, 2] + * + * // from an async iterable + * var asyncIterable = { + * [Symbol.asyncIterable]: generator + * }; + * var stream = _(asyncIterable).toArray(_.log); + * //=> [1, 2] */ /*eslint-disable no-multi-spaces */ @@ -259,13 +280,17 @@ function __(StreamCtor) { // their Symbol.iterator method returns the `this` object // and an infinite loop would result otherwise else if (_.isFunction(xs.next)) { - //probably an iterator + // probably an iterator. This handle both sync and async iterators. return iteratorStream(StreamCtor, xs); } else if (!_.isUndefined(_global.Symbol) && xs[_global.Symbol.iterator]) { - //probably an iterable + // probably an iterable return iteratorStream(StreamCtor, xs[_global.Symbol.iterator]()); } + else if (!_.isUndefined(_global.Symbol) && xs[_global.Symbol.asyncIterator]) { + // probably an async iterable + return iteratorStream(StreamCtor, xs[_global.Symbol.asyncIterator]()); + } else { throw new Error( 'Object was not a stream, promise, iterator or iterable: ' + (typeof xs) diff --git a/test/test.js b/test/test.js index 5aa6680..6d3f4f3 100755 --- a/test/test.js +++ b/test/test.js @@ -1333,6 +1333,16 @@ exports.constructor = { }, }; +// Async iterable tests +if (global.Symbol && global.Symbol.asyncIterator) { + exports.constructor['from async iterable'] = function (test) { + var asyncIterable = {}; + asyncIterable[global.Symbol.asyncIterator] = + this.createTestAsyncIterator.bind(this, [1, 2, 3]); + _(asyncIterable).toArray(this.tester([1, 2, 3], test)); + }; +} + exports.GeneratorStream = { 'sync next does not re-enter generator': function (test) { test.expect(2);