From 817c4ddc0e7b6fb076181f7799caa50556a47a30 Mon Sep 17 00:00:00 2001 From: Denis Pushkarev Date: Sun, 19 Sep 2021 02:10:43 +0700 Subject: [PATCH] add `Array.fromAsync` proposal https://github.com/tc39/proposal-array-from-async --- CHANGELOG.md | 3 + README.md | 22 ++++++ packages/core-js-compat/src/data.mjs | 4 ++ .../src/modules-by-versions.mjs | 4 ++ .../modules/esnext.typed-array.from-async.js | 1 + packages/core-js/features/array/from-async.js | 8 +++ packages/core-js/features/array/index.js | 2 + .../features/typed-array/from-async.js | 1 + .../core-js/features/typed-array/index.js | 2 + .../core-js/internals/array-from-async.js | 32 +++++++++ .../internals/async-iterator-iteration.js | 31 ++++++--- .../modules/esnext.array.from-async.js | 8 +++ .../modules/esnext.async-iterator.to-array.js | 2 +- .../modules/esnext.typed-array.from-async.js | 19 ++++++ .../core-js/proposals/array-from-async.js | 3 + packages/core-js/stage/1.js | 1 + tests/commonjs.mjs | 3 + tests/compat/tests.js | 6 ++ tests/helpers/helpers.js | 24 +++++++ tests/pure/esnext.array.from-async.js | 65 ++++++++++++++++++ tests/tests/esnext.array.from-async.js | 66 ++++++++++++++++++ tests/tests/esnext.typed-array.from-async.js | 67 +++++++++++++++++++ 22 files changed, 363 insertions(+), 11 deletions(-) create mode 100644 packages/core-js-pure/override/modules/esnext.typed-array.from-async.js create mode 100644 packages/core-js/features/array/from-async.js create mode 100644 packages/core-js/features/typed-array/from-async.js create mode 100644 packages/core-js/internals/array-from-async.js create mode 100644 packages/core-js/modules/esnext.array.from-async.js create mode 100644 packages/core-js/modules/esnext.typed-array.from-async.js create mode 100644 packages/core-js/proposals/array-from-async.js create mode 100644 tests/pure/esnext.array.from-async.js create mode 100644 tests/tests/esnext.array.from-async.js create mode 100644 tests/tests/esnext.typed-array.from-async.js diff --git a/CHANGELOG.md b/CHANGELOG.md index f5336305e511..03e9c0d56e6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## Changelog ##### Unreleased +- Added [`Array.fromAsync` stage 1 proposal](https://github.com/tc39/proposal-array-from-async): + - `Array.fromAsync` + - `%TypedArray%.fromAsync` - `.name` and `.toString()` on polyfilled functions improved in many different cases - Improved internal `IsConstructor` and `IsCallable` checks - Fixed some internal cases of `GetMethod` operation diff --git a/README.md b/README.md index be9ebd5e2ecc..53444092e004 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ Promise.resolve(32).then(x => console.log(x)); // => 32 - [New collections methods](#new-collections-methods) - [`.of` and `.from` methods on collection constructors](#of-and-from-methods-on-collection-constructors) - [`compositeKey` and `compositeSymbol`](#compositekey-and-compositesymbol) + - [`Array.fromAsync`](#arrayfromasync) - [`Array` filtering](#array-filtering) - [`Array` grouping](#array-grouping) - [`Array` deduplication](#array-deduplication) @@ -2297,6 +2298,27 @@ console.log(compositeSymbol(1, a) === compositeSymbol(1, a)); // => true console.log(compositeSymbol(1, a, 2, b) === compositeSymbol(1, a, 2, b)); // => true console.log(compositeSymbol(a, a) === compositeSymbol(a, a)); // => true ``` +##### [`Array.fromAsync`](https://github.com/tc39/proposal-array-from-async)[⬆](#index) +Modules [`esnext.array.from-async`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.array.from-async.js) and [`esnext.typed-array.from-async`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.typed-array.from-async.js) +```js +class Array { + static fromAsync(asyncItems: AsyncIterable | Iterable | ArrayLike, mapfn?: (value: any, index: number) => any, thisArg?: any): Array; +} + +class %TypedArray% { + static fromAsync(asyncItems: AsyncIterable | Iterable | ArrayLike, mapfn?: (value: number, index: number, target) => number, thisArg?: any): %TypedArray%; +} +``` +[*CommonJS entry points:*](#commonjs-api) +```js +core-js/proposals/array-from-async +core-js(-pure)/features/array/from-async +core-js/features/typed-array/from-async +``` +[*Example*](https://goo.gl/Jt7SsD): +```js +await Array.fromAsync((async function * (){ yield * [1, 2, 3] })(), i => i * i); // => [1, 4, 9] +``` ##### [Array filtering](https://github.com/tc39/proposal-array-filtering)[⬆](#index) Modules [`esnext.array.filter-reject`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.array.filter-reject.js) and [`esnext.typed-array.filter-reject`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.typed-array.filter-reject.js). ```js diff --git a/packages/core-js-compat/src/data.mjs b/packages/core-js-compat/src/data.mjs index 91cb96b37ed8..1f4a90ecc734 100644 --- a/packages/core-js-compat/src/data.mjs +++ b/packages/core-js-compat/src/data.mjs @@ -1428,6 +1428,8 @@ export const data = { }, // TODO: Remove from `core-js@4` 'esnext.aggregate-error': null, + 'esnext.array.from-async': { + }, // TODO: Remove from `core-js@4` 'esnext.array.at': null, // TODO: Remove from `core-js@4` @@ -1700,6 +1702,8 @@ export const data = { // TODO: Remove from `core-js@4` 'esnext.symbol.replace-all': { }, + 'esnext.typed-array.from-async': { + }, // TODO: Remove from `core-js@4` 'esnext.typed-array.at': null, // TODO: Remove from `core-js@4` diff --git a/packages/core-js-compat/src/modules-by-versions.mjs b/packages/core-js-compat/src/modules-by-versions.mjs index 5097e3ed8beb..480a457cdacf 100644 --- a/packages/core-js-compat/src/modules-by-versions.mjs +++ b/packages/core-js-compat/src/modules-by-versions.mjs @@ -107,4 +107,8 @@ export default { 'es.string.at-alternative', 'es.typed-array.at', ], + 3.18: [ + 'esnext.array.from-async', + 'esnext.typed-array.from-async', + ], }; diff --git a/packages/core-js-pure/override/modules/esnext.typed-array.from-async.js b/packages/core-js-pure/override/modules/esnext.typed-array.from-async.js new file mode 100644 index 000000000000..8b1a393741c9 --- /dev/null +++ b/packages/core-js-pure/override/modules/esnext.typed-array.from-async.js @@ -0,0 +1 @@ +// empty diff --git a/packages/core-js/features/array/from-async.js b/packages/core-js/features/array/from-async.js new file mode 100644 index 000000000000..64488d02b5ab --- /dev/null +++ b/packages/core-js/features/array/from-async.js @@ -0,0 +1,8 @@ +require('../../modules/es.array.iterator'); +require('../../modules/es.object.to-string'); +require('../../modules/es.promise'); +require('../../modules/es.string.iterator'); +require('../../modules/esnext.array.from-async'); +var path = require('../../internals/path'); + +module.exports = path.Array.fromAsync; diff --git a/packages/core-js/features/array/index.js b/packages/core-js/features/array/index.js index 08e0c0cf652f..3073cc62cfef 100644 --- a/packages/core-js/features/array/index.js +++ b/packages/core-js/features/array/index.js @@ -1,5 +1,7 @@ var parent = require('../../stable/array'); require('../../modules/es.map'); +require('../../modules/es.promise'); +require('../../modules/esnext.array.from-async'); // TODO: Remove from `core-js@4` require('../../modules/esnext.array.at'); // TODO: Remove from `core-js@4` diff --git a/packages/core-js/features/typed-array/from-async.js b/packages/core-js/features/typed-array/from-async.js new file mode 100644 index 000000000000..e79dd46c554a --- /dev/null +++ b/packages/core-js/features/typed-array/from-async.js @@ -0,0 +1 @@ +require('../../modules/esnext.typed-array.from-async'); diff --git a/packages/core-js/features/typed-array/index.js b/packages/core-js/features/typed-array/index.js index bb01604a1d32..523da03b7115 100644 --- a/packages/core-js/features/typed-array/index.js +++ b/packages/core-js/features/typed-array/index.js @@ -1,5 +1,7 @@ var parent = require('../../stable/typed-array'); require('../../modules/es.map'); +require('../../modules/es.promise'); +require('../../modules/esnext.typed-array.from-async'); // TODO: Remove from `core-js@4` require('../../modules/esnext.typed-array.at'); // TODO: Remove from `core-js@4` diff --git a/packages/core-js/internals/array-from-async.js b/packages/core-js/internals/array-from-async.js new file mode 100644 index 000000000000..394d6f66309f --- /dev/null +++ b/packages/core-js/internals/array-from-async.js @@ -0,0 +1,32 @@ +'use strict'; +var bind = require('../internals/function-bind-context'); +var toObject = require('../internals/to-object'); +var isConstructor = require('../internals/is-constructor'); +var getAsyncIterator = require('../internals/get-async-iterator'); +var getIterator = require('../internals/get-iterator'); +var getIteratorMethod = require('../internals/get-iterator-method'); +var getMethod = require('../internals/get-method'); +var getVirtual = require('../internals/entry-virtual'); +var wellKnownSymbol = require('../internals/well-known-symbol'); +var AsyncFromSyncIterator = require('../internals/async-from-sync-iterator'); +var toArray = require('../internals/async-iterator-iteration').toArray; + +var ASYNC_ITERATOR = wellKnownSymbol('asyncIterator'); +var arrayIterator = getVirtual('Array').values; + +// `Array.fromAsync` method implementation +// https://github.com/tc39/proposal-array-from-async +module.exports = function fromAsync(asyncItems /* , mapfn = undefined, thisArg = undefined */) { + var O = toObject(asyncItems); + var argumentsLength = arguments.length; + var mapfn = argumentsLength > 1 ? arguments[1] : undefined; + if (mapfn !== undefined) mapfn = bind(mapfn, argumentsLength > 2 ? arguments[2] : undefined, 2); + var usingAsyncIterator = getMethod(O, ASYNC_ITERATOR); + var usingIterator; + if (!usingAsyncIterator) usingIterator = getIteratorMethod(O); + var A = isConstructor(this) ? new this() : []; + var iterator = usingAsyncIterator + ? getAsyncIterator(O, usingAsyncIterator) + : new AsyncFromSyncIterator(getIterator(O, usingIterator || arrayIterator)); + return toArray(iterator, mapfn, A); +}; diff --git a/packages/core-js/internals/async-iterator-iteration.js b/packages/core-js/internals/async-iterator-iteration.js index b7d2f3ad5b31..47b421de1549 100644 --- a/packages/core-js/internals/async-iterator-iteration.js +++ b/packages/core-js/internals/async-iterator-iteration.js @@ -5,7 +5,7 @@ var anObject = require('../internals/an-object'); var getBuiltIn = require('../internals/get-built-in'); var getMethod = require('../internals/get-method'); -var Promise = getBuiltIn('Promise'); +var MAX_SAFE_INTEGER = 9007199254740991; var push = [].push; var createMethod = function (TYPE) { @@ -13,11 +13,13 @@ var createMethod = function (TYPE) { var IS_FOR_EACH = TYPE == 1; var IS_EVERY = TYPE == 2; var IS_SOME = TYPE == 3; - return function (iterator, fn) { + return function (iterator, fn, target) { anObject(iterator); + var Promise = getBuiltIn('Promise'); var next = aCallable(iterator.next); - var array = IS_TO_ARRAY ? [] : undefined; - if (!IS_TO_ARRAY) aCallable(fn); + var counter = -1; + var MAPPING = fn !== undefined; + if (MAPPING || !IS_TO_ARRAY) aCallable(fn); return new Promise(function (resolve, reject) { var closeIteration = function (method, argument) { @@ -41,25 +43,34 @@ var createMethod = function (TYPE) { var loop = function () { try { + if (IS_TO_ARRAY && (++counter > MAX_SAFE_INTEGER) && MAPPING) { + throw TypeError('The allowed number of iterations has been exceeded'); + } Promise.resolve(anObject(next.call(iterator))).then(function (step) { try { if (anObject(step).done) { - resolve(IS_TO_ARRAY ? array : IS_SOME ? false : IS_EVERY || undefined); + if (IS_TO_ARRAY) { + target.length = counter; + resolve(target); + } else resolve(IS_SOME ? false : IS_EVERY || undefined); } else { var value = step.value; - if (IS_TO_ARRAY) { - push.call(array, value); - loop(); - } else { - Promise.resolve(fn(value)).then(function (result) { + if (MAPPING) { + Promise.resolve(IS_TO_ARRAY ? fn(value, counter) : fn(value)).then(function (result) { if (IS_FOR_EACH) { loop(); } else if (IS_EVERY) { result ? loop() : closeIteration(resolve, false); + } else if (IS_TO_ARRAY) { + push.call(target, result); + loop(); } else { result ? closeIteration(resolve, IS_SOME || value) : loop(); } }, onError); + } else { + push.call(target, value); + loop(); } } } catch (error) { onError(error); } diff --git a/packages/core-js/modules/esnext.array.from-async.js b/packages/core-js/modules/esnext.array.from-async.js new file mode 100644 index 000000000000..30b7155c0fe4 --- /dev/null +++ b/packages/core-js/modules/esnext.array.from-async.js @@ -0,0 +1,8 @@ +var $ = require('../internals/export'); +var fromAsync = require('../internals/array-from-async'); + +// `Array.fromAsync` method +// https://github.com/tc39/proposal-array-from-async +$({ target: 'Array', stat: true }, { + fromAsync: fromAsync +}); diff --git a/packages/core-js/modules/esnext.async-iterator.to-array.js b/packages/core-js/modules/esnext.async-iterator.to-array.js index 04e3e7f8eec8..2027325d5a3a 100644 --- a/packages/core-js/modules/esnext.async-iterator.to-array.js +++ b/packages/core-js/modules/esnext.async-iterator.to-array.js @@ -5,6 +5,6 @@ var $toArray = require('../internals/async-iterator-iteration').toArray; $({ target: 'AsyncIterator', proto: true, real: true }, { toArray: function toArray() { - return $toArray(this); + return $toArray(this, undefined, []); } }); diff --git a/packages/core-js/modules/esnext.typed-array.from-async.js b/packages/core-js/modules/esnext.typed-array.from-async.js new file mode 100644 index 000000000000..a1b3446a03fa --- /dev/null +++ b/packages/core-js/modules/esnext.typed-array.from-async.js @@ -0,0 +1,19 @@ +'use strict'; +var aConstructor = require('../internals/a-constructor'); +var arrayFromAsync = require('../internals/array-from-async'); +var TYPED_ARRAYS_CONSTRUCTORS_REQUIRES_WRAPPERS = require('../internals/typed-array-constructors-require-wrappers'); +var ArrayBufferViewCore = require('../internals/array-buffer-view-core'); +var arrayFromConstructorAndList = require('../internals/array-from-constructor-and-list'); + +var aTypedArrayConstructor = ArrayBufferViewCore.aTypedArrayConstructor; +var exportTypedArrayStaticMethod = ArrayBufferViewCore.exportTypedArrayStaticMethod; + +// `%TypedArray%.fromAsync` method +// https://github.com/tc39/proposal-array-from-async +// eslint-disable-next-line -- required for .length +exportTypedArrayStaticMethod('fromAsync', function fromAsync(asyncItems /* , mapfn = undefined, thisArg = undefined */) { + var C = aConstructor(this); + return arrayFromAsync.apply(Array, arguments).then(function (list) { + return arrayFromConstructorAndList(aTypedArrayConstructor(C), list); + }); +}, TYPED_ARRAYS_CONSTRUCTORS_REQUIRES_WRAPPERS); diff --git a/packages/core-js/proposals/array-from-async.js b/packages/core-js/proposals/array-from-async.js new file mode 100644 index 000000000000..6fb9c182ff16 --- /dev/null +++ b/packages/core-js/proposals/array-from-async.js @@ -0,0 +1,3 @@ +// https://github.com/tc39/proposal-array-from-async +require('../modules/esnext.array.from-async'); +require('../modules/esnext.typed-array.from-async'); diff --git a/packages/core-js/stage/1.js b/packages/core-js/stage/1.js index 2907a863af48..862f7eb11122 100644 --- a/packages/core-js/stage/1.js +++ b/packages/core-js/stage/1.js @@ -1,4 +1,5 @@ require('../proposals/array-filtering'); +require('../proposals/array-from-async'); require('../proposals/array-grouping'); require('../proposals/array-last'); require('../proposals/array-unique'); diff --git a/tests/commonjs.mjs b/tests/commonjs.mjs index c65ff944747d..90b8883659f5 100644 --- a/tests/commonjs.mjs +++ b/tests/commonjs.mjs @@ -561,6 +561,7 @@ for (PATH of ['core-js-pure', 'core-js']) { const Set = load(NS, 'set'); const WeakMap = load(NS, 'weak-map'); const WeakSet = load(NS, 'weak-set'); + ok(typeof load(NS, 'array/from-async') === 'function'); ok(typeof load(NS, 'array/filter-out') === 'function'); ok(typeof load(NS, 'array/filter-reject') === 'function'); ok(load(NS, 'array/find-last')([1, 2, 3], it => it % 2) === 3); @@ -741,6 +742,7 @@ for (PATH of ['core-js-pure', 'core-js']) { load('proposals/array-last'); load('proposals/array-filtering'); load('proposals/array-find-from-last'); + load('proposals/array-from-async'); load('proposals/array-grouping'); load('proposals/array-is-template-object'); load('proposals/array-unique'); @@ -852,6 +854,7 @@ for (const NS of ['es', 'stable', 'features']) { { const NS = 'features'; + load(NS, 'typed-array/from-async'); load(NS, 'typed-array/filter-out'); load(NS, 'typed-array/filter-reject'); load(NS, 'typed-array/find-last'); diff --git a/tests/compat/tests.js b/tests/compat/tests.js index 44c5971bacad..75b37344293b 100644 --- a/tests/compat/tests.js +++ b/tests/compat/tests.js @@ -1198,6 +1198,9 @@ GLOBAL.tests = { && set.add({}) == set && set[Symbol.toStringTag]; }], + 'esnext.array.from-async': function () { + return Array.fromAsync; + }, 'esnext.array.filter-reject': function () { return [].filterReject; }, @@ -1470,6 +1473,9 @@ GLOBAL.tests = { 'esnext.symbol.observable': function () { return Symbol.observable; }, + 'esnext.typed-array.from-async': function () { + return Int8Array.fromAsync; + }, 'esnext.typed-array.filter-reject': function () { return Int8Array.prototype.filterReject; }, diff --git a/tests/helpers/helpers.js b/tests/helpers/helpers.js index 5392b93756de..21f3e3924d4d 100644 --- a/tests/helpers/helpers.js +++ b/tests/helpers/helpers.js @@ -1,5 +1,6 @@ import Promise from 'core-js-pure/es/promise'; import ITERATOR from 'core-js-pure/features/symbol/iterator'; +import ASYNC_ITERATOR from 'core-js-pure/features/symbol/async-iterator'; export function createIterator(elements, methods) { let index = 0; @@ -40,6 +41,29 @@ export function createIterable(elements, methods) { return iterable; } +export function createAsyncIterable(elements, methods) { + const iterable = { + called: false, + received: false, + [ASYNC_ITERATOR]() { + iterable.received = true; + let index = 0; + const iterator = { + next() { + iterable.called = true; + return Promise.resolve({ + value: elements[index++], + done: index > elements.length, + }); + }, + }; + if (methods) for (const key in methods) iterator[key] = methods[key]; + return iterator; + }, + }; + return iterable; +} + export function includes(target, wanted) { for (const element of target) if (wanted === element) return true; return false; diff --git a/tests/pure/esnext.array.from-async.js b/tests/pure/esnext.array.from-async.js new file mode 100644 index 000000000000..41bb264c74ab --- /dev/null +++ b/tests/pure/esnext.array.from-async.js @@ -0,0 +1,65 @@ +import { createAsyncIterable, createIterable } from '../helpers/helpers'; +import { STRICT_THIS } from '../helpers/constants'; +import Promise from 'core-js-pure/es/promise'; +import fromAsync from 'core-js-pure/features/array/from-async'; + +QUnit.test('Array.fromAsync', assert => { + assert.expect(25); + const async = assert.async(); + + assert.isFunction(fromAsync); + assert.arity(fromAsync, 1); + assert.name(fromAsync, 'fromAsync'); + + function C() { /* empty */ } + + fromAsync(createAsyncIterable([1, 2, 3]), it => it ** 2).then(it => { + assert.arrayEqual(it, [1, 4, 9], 'async iterable and mapfn'); + return fromAsync(createAsyncIterable([1]), function (arg, index) { + assert.same(this, STRICT_THIS, 'this'); + assert.same(arguments.length, 2, 'arguments length'); + assert.same(arg, 1, 'argument'); + assert.same(index, 0, 'index'); + }); + }).then(() => { + return fromAsync(createAsyncIterable([1, 2, 3])); + }).then(it => { + assert.arrayEqual(it, [1, 2, 3], 'async iterable without mapfn'); + return fromAsync(createIterable([1, 2, 3]), arg => arg ** 2); + }).then(it => { + assert.arrayEqual(it, [1, 4, 9], 'iterable and mapfn'); + return fromAsync(createIterable([1, 2, 3]), arg => Promise.resolve(arg ** 2)); + }).then(it => { + assert.arrayEqual(it, [1, 4, 9], 'iterable and async mapfn'); + return fromAsync(createIterable([1]), function (arg, index) { + assert.same(this, STRICT_THIS, 'this'); + assert.same(arguments.length, 2, 'arguments length'); + assert.same(arg, 1, 'argument'); + assert.same(index, 0, 'index'); + }); + }).then(() => { + return fromAsync(createIterable([1, 2, 3])); + }).then(it => { + assert.arrayEqual(it, [1, 2, 3], 'iterable and without mapfn'); + return fromAsync([1, Promise.resolve(2), 3]); + }).then(it => { + assert.arrayEqual(it, [1, 2, 3], 'array'); + return fromAsync('123'); + }).then(it => { + assert.arrayEqual(it, ['1', '2', '3'], 'string'); + return fromAsync.call(C, [1]); + }).then(it => { + assert.ok(it instanceof C, 'subclassable'); + return fromAsync({ length: 1, 0: 1 }); + }).then(it => { + assert.arrayEqual(it, [1], 'non-iterable'); + return fromAsync(createIterable([1]), () => { throw 42; }); + }).catch(error => { + assert.same(error, 42, 'rejection on a callback error'); + }).then(() => async()); + + assert.throws(() => fromAsync(undefined, () => { /* empty */ }), TypeError); + assert.throws(() => fromAsync(null, () => { /* empty */ }), TypeError); + assert.throws(() => fromAsync([1], null), TypeError); + assert.throws(() => fromAsync([1], {}), TypeError); +}); diff --git a/tests/tests/esnext.array.from-async.js b/tests/tests/esnext.array.from-async.js new file mode 100644 index 000000000000..b5364abd6cc6 --- /dev/null +++ b/tests/tests/esnext.array.from-async.js @@ -0,0 +1,66 @@ +import { createAsyncIterable, createIterable } from '../helpers/helpers'; +import { STRICT_THIS } from '../helpers/constants'; + +QUnit.test('Array.fromAsync', assert => { + assert.expect(27); + const async = assert.async(); + const { fromAsync } = Array; + + assert.isFunction(fromAsync); + assert.arity(fromAsync, 1); + assert.name(fromAsync, 'fromAsync'); + assert.looksNative(fromAsync); + assert.nonEnumerable(Array, 'fromAsync'); + + function C() { /* empty */ } + + fromAsync(createAsyncIterable([1, 2, 3]), it => it ** 2).then(it => { + assert.arrayEqual(it, [1, 4, 9], 'async iterable and mapfn'); + return fromAsync(createAsyncIterable([1]), function (arg, index) { + assert.same(this, STRICT_THIS, 'this'); + assert.same(arguments.length, 2, 'arguments length'); + assert.same(arg, 1, 'argument'); + assert.same(index, 0, 'index'); + }); + }).then(() => { + return fromAsync(createAsyncIterable([1, 2, 3])); + }).then(it => { + assert.arrayEqual(it, [1, 2, 3], 'async iterable without mapfn'); + return fromAsync(createIterable([1, 2, 3]), arg => arg ** 2); + }).then(it => { + assert.arrayEqual(it, [1, 4, 9], 'iterable and mapfn'); + return fromAsync(createIterable([1, 2, 3]), arg => Promise.resolve(arg ** 2)); + }).then(it => { + assert.arrayEqual(it, [1, 4, 9], 'iterable and async mapfn'); + return fromAsync(createIterable([1]), function (arg, index) { + assert.same(this, STRICT_THIS, 'this'); + assert.same(arguments.length, 2, 'arguments length'); + assert.same(arg, 1, 'argument'); + assert.same(index, 0, 'index'); + }); + }).then(() => { + return fromAsync(createIterable([1, 2, 3])); + }).then(it => { + assert.arrayEqual(it, [1, 2, 3], 'iterable and without mapfn'); + return fromAsync([1, Promise.resolve(2), 3]); + }).then(it => { + assert.arrayEqual(it, [1, 2, 3], 'array'); + return fromAsync('123'); + }).then(it => { + assert.arrayEqual(it, ['1', '2', '3'], 'string'); + return fromAsync.call(C, [1]); + }).then(it => { + assert.ok(it instanceof C, 'subclassable'); + return fromAsync({ length: 1, 0: 1 }); + }).then(it => { + assert.arrayEqual(it, [1], 'non-iterable'); + return fromAsync(createIterable([1]), () => { throw 42; }); + }).catch(error => { + assert.same(error, 42, 'rejection on a callback error'); + }).then(() => async()); + + assert.throws(() => fromAsync(undefined, () => { /* empty */ }), TypeError); + assert.throws(() => fromAsync(null, () => { /* empty */ }), TypeError); + assert.throws(() => fromAsync([1], null), TypeError); + assert.throws(() => fromAsync([1], {}), TypeError); +}); diff --git a/tests/tests/esnext.typed-array.from-async.js b/tests/tests/esnext.typed-array.from-async.js new file mode 100644 index 000000000000..12434c815ead --- /dev/null +++ b/tests/tests/esnext.typed-array.from-async.js @@ -0,0 +1,67 @@ +import { createAsyncIterable, createIterable } from '../helpers/helpers'; +import { DESCRIPTORS, GLOBAL, STRICT_THIS, TYPED_ARRAYS } from '../helpers/constants'; + +if (DESCRIPTORS) { + // we can't implement %TypedArray% in all engines, so run all tests for each typed array constructor + for (const name in TYPED_ARRAYS) QUnit.test(`${ TYPED_ARRAYS }.fromAsync`, assert => { + assert.expect(26); + // eslint-disable-next-line qunit/no-async-in-loops -- safe + const async = assert.async(); + const TypedArray = GLOBAL[name]; + const { fromAsync } = TypedArray; + + assert.isFunction(fromAsync); + assert.arity(fromAsync, 1); + assert.name(fromAsync, 'fromAsync'); + assert.looksNative(fromAsync); + + TypedArray.fromAsync(createAsyncIterable([1, 2, 3]), it => it ** 2).then(it => { + assert.arrayEqual(it, [1, 4, 9], 'async iterable and mapfn'); + return TypedArray.fromAsync(createAsyncIterable([1]), function (arg, index) { + assert.same(this, STRICT_THIS, 'this'); + assert.same(arguments.length, 2, 'arguments length'); + assert.same(arg, 1, 'argument'); + assert.same(index, 0, 'index'); + }); + }).then(() => { + return TypedArray.fromAsync(createAsyncIterable([1, 2, 3])); + }).then(it => { + assert.arrayEqual(it, [1, 2, 3], 'async iterable without mapfn'); + return TypedArray.fromAsync(createIterable([1, 2, 3]), arg => arg ** 2); + }).then(it => { + assert.arrayEqual(it, [1, 4, 9], 'iterable and mapfn'); + return TypedArray.fromAsync(createIterable([1, 2, 3]), arg => Promise.resolve(arg ** 2)); + }).then(it => { + assert.arrayEqual(it, [1, 4, 9], 'iterable and async mapfn'); + return TypedArray.fromAsync(createIterable([1]), function (arg, index) { + assert.same(this, STRICT_THIS, 'this'); + assert.same(arguments.length, 2, 'arguments length'); + assert.same(arg, 1, 'argument'); + assert.same(index, 0, 'index'); + }); + }).then(() => { + return TypedArray.fromAsync(createIterable([1, 2, 3])); + }).then(it => { + assert.arrayEqual(it, [1, 2, 3], 'iterable and without mapfn'); + return TypedArray.fromAsync([1, Promise.resolve(2), 3]); + }).then(it => { + assert.arrayEqual(it, [1, 2, 3], 'array'); + return TypedArray.fromAsync('123'); + }).then(it => { + assert.arrayEqual(it, [1, 2, 3], 'string'); + return TypedArray.fromAsync({ length: 1, 0: 1 }); + }).then(it => { + assert.arrayEqual(it, [1], 'non-iterable'); + return TypedArray.fromAsync(createIterable([1]), () => { throw 42; }); + }).catch(error => { + assert.same(error, 42, 'rejection on a callback error'); + }).then(() => async()); + + function C() { /* empty */ } + assert.throws(() => TypedArray.fromAsync.call(C, [1], {}), TypeError); + assert.throws(() => TypedArray.fromAsync(undefined, () => { /* empty */ }), TypeError); + assert.throws(() => TypedArray.fromAsync(null, () => { /* empty */ }), TypeError); + assert.throws(() => TypedArray.fromAsync([1], null), TypeError); + assert.throws(() => TypedArray.fromAsync([1], {}), TypeError); + }); +}