diff --git a/src/index.js b/src/index.js index 0931ef0b1a..e52c9b7da8 100644 --- a/src/index.js +++ b/src/index.js @@ -10,6 +10,7 @@ export { default as isNotNull } from './isNotNull'; export { default as isNotNil } from './isNotNil'; export { default as isArray } from './isArray'; export { default as isIterable } from './isIterable'; +export { default as isAsyncIterable } from './isAsyncIterable'; export { default as isEmptyArray } from './isEmptyArray'; export { default as isNotArray } from './isNotArray'; export { default as isNonEmptyArray } from './isNonEmptyArray'; diff --git a/src/isAsyncIterable.js b/src/isAsyncIterable.js new file mode 100644 index 0000000000..bd16132e3e --- /dev/null +++ b/src/isAsyncIterable.js @@ -0,0 +1,41 @@ +import { hasIn, curryN } from 'ramda'; + +import isFunction from './isFunction'; + +/** + * Checks whether the passed value is async iterable. + * + * @func isAsyncIterable + * @memberOf RA + * @since {@link https://char0n.github.io/ramda-adjunct/3.2.0|v3.2.0} + * @category Type + * @sig * -> Boolean + * @param {*} val The value to test + * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator} + * @return {boolean} + * @example + * + * RA.isAsyncIterable({ + * async* [Symbol.asyncIterator]() { + * yield "Blade"; + * yield "Runner" + * } + * }); //=> true + * + * RA.isAsyncIterable({}); //=> false + * RA.isAsyncIterable(-0); //=> false + * RA.isAsyncIterable(null); //=> false + * RA.isAsyncIterable(undefined); //=> false + */ +const isAsyncIterable = curryN(1, (val) => { + if (typeof Symbol === 'undefined') { + return false; + } + + return ( + hasIn(Symbol.asyncIterator, Object(val)) && + isFunction(val[Symbol.asyncIterator]) + ); +}); + +export default isAsyncIterable; diff --git a/test/isAsyncIterable.js b/test/isAsyncIterable.js new file mode 100644 index 0000000000..87a109a473 --- /dev/null +++ b/test/isAsyncIterable.js @@ -0,0 +1,89 @@ +import { assert } from 'chai'; +import * as R from 'ramda'; + +import * as RA from '../src'; + +describe('isAsyncIterable', function () { + context('given the item is an array with items', function () { + specify('should return false', function () { + assert.isFalse(RA.isAsyncIterable(['arrays', 'are', 'iterable'])); + }); + }); + + context( + 'given the item is an Object implementing an asyncIterator', + function () { + specify('should return true', function () { + const asyncIterable = { + async *[Symbol.asyncIterator]() { + yield 'Blade'; + yield 'Runner'; + }, + }; + assert.isTrue(RA.isAsyncIterable(asyncIterable)); + }); + } + ); + + context('given the value undefined', function () { + specify('should return false', function () { + assert.isFalse(RA.isAsyncIterable(undefined)); + }); + }); + + context('given a number', function () { + specify('should return false', function () { + assert.isFalse(RA.isAsyncIterable(42)); + }); + }); + + context('given the value null', function () { + specify('should return false', function () { + assert.isFalse(RA.isAsyncIterable(null)); + }); + }); + + context('given the value NaN', function () { + specify('should return false', function () { + assert.isFalse(RA.isAsyncIterable(NaN)); + }); + }); + + context('given the value Infinity', function () { + specify('should return false', function () { + assert.isFalse(RA.isAsyncIterable(Infinity)); + }); + }); + + context('given a boolean value', function () { + specify('should return false', function () { + assert.isFalse(RA.isAsyncIterable(true)); + }); + }); + + context('given the value -0', function () { + specify('should return false', function () { + assert.isFalse(RA.isAsyncIterable(-0)); + }); + }); + + context('given an empty object', function () { + specify('should return false', function () { + assert.isFalse(RA.isAsyncIterable({})); + }); + }); + + context('should support placeholder to specify "gaps"', function () { + specify('should return false', function () { + const isAsyncIterable = RA.isAsyncIterable(R.__); + const asyncIterable = { + async *[Symbol.asyncIterator]() { + yield 'Blade'; + yield 'Runner'; + }, + }; + + assert.isTrue(isAsyncIterable(asyncIterable)); + }); + }); +});