Skip to content

Commit

Permalink
explainer/spec: Sync iterators and array-like objects
Browse files Browse the repository at this point in the history
Fixes #6. Also addresses #7.
  • Loading branch information
js-choi committed Sep 18, 2021
1 parent a76f4ea commit 64a4921
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 17 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ Similarly to **[`Array.from`][]**,
of the `Array` built-in class, with **one required argument**
and **two optional arguments**: `(items, mapfn, thisArg)`.

But instead of converting an **iterable** to an array,
it converts an **async iterable** to a **promise**
that will resolve to an array.
But instead of converting an **array-like object** or **iterable** to an array,
it converts an **async iterable** (or array-like object or iterable)
to a **promise** that will resolve to an array.

```js
async function * f () {
Expand Down
47 changes: 33 additions & 14 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,20 @@ <h1><ins>Array.fromAsync ( _asyncItems_ [ , _mapfn_ [ , _thisArg_ ] ] )</ins></h
<emu-alg>
1. Let _C_ be the *this* value.
1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%).
1. If _mapfn_ is *undefined*, let _mapping_ be *false*.
1. Else,
1. If IsCallable(_mapfn_) is *false*, throw a *TypeError* exception.
1. Let _mapping_ be *true*.
1. Let _usingAsyncIterator_ be ? GetMethod(_asyncItems_, @@asyncIterator).
1. If _usingAsyncIterator_ is *undefined*, throw a *TypeError* exception.
1. Else,
1. If IsConstructor(_C_) is *true*, then
1. Let _A_ be ? Construct(_C_).
1. Let _fromAsyncClosure_ be a new Abstract Closure with no parameters that captures _C_ and _mapfn_ and performs the following steps when called:
1. If _mapfn_ is *undefined*, let _mapping_ be *false*.
1. Else,
1. If IsCallable(_mapfn_) is *false*, throw a *TypeError* exception.
1. Let _mapping_ be *true*.
1. Let _usingAsyncOrSyncIterator_ be ? GetMethod(_asyncItems_, @@asyncIterator).
1. If _usingAsyncOrSyncIterator_ is *undefined*, let _usingAsyncOrSyncIterator_ be ? GetMethod(_asyncItems_, @@iterator).
1. If _usingAsyncOrSyncIterator_ is *undefined*, throw a *TypeError* exception.
1. Else,
1. Let _A_ be ! ArrayCreate(0).
1. Let _iteratorRecord_ be ? GetIterator(_asyncItems_, ~async~, _usingAsyncIterator_).
1. Let _asyncSpreadClosure_ be a new Abstract Closure with no parameters that captures _A_ and _mapfn_ and performs the following steps when called:
1. If IsConstructor(_C_) is *true*, then
1. Let _A_ be ? Construct(_C_).
1. Else,
1. Let _A_ be ! ArrayCreate(0).
1. Let _iteratorRecord_ be ? GetIterator(_asyncItems_, ~async~, _usingAsyncOrSyncIterator_).

This comment has been minimized.

Copy link
@zloirock

zloirock Sep 18, 2021

Contributor

@js-choi this fallback will not properly work with sync iterators.

await Array.fromAsync([Promise.resolve(1), 2, Promise.resolve(3)]); // => [Promise.resolve(1), 2, Promise.resolve(3)]

Something like that could be simpler.

Let usingAsyncIterator be ? GetMethod(asyncItems, @@asyncIterator).
Let usingIterator be undefined.
If usingAsyncIterator is undefined,
  Let usingIterator be ? GetMethod(asyncItems, @@Iterator).
  If usingIterator is undefined,
    Let usingIterator be ? %Array.prototype.values%.
...
If usingAsyncIterator is not undefined,
  Let iteratorRecord be ? GetIterator(asyncItems, async, usingAsyncIterator).
Else,
  Let syncIteratorRecord be ? GetIterator(asyncItems, sync, usingIterator).
  Let iteratorRecord be ? CreateAsyncFromSyncIterator(syncIteratorRecord).

This comment has been minimized.

Copy link
@js-choi

js-choi Sep 19, 2021

Author Collaborator

Thanks for the catch. I opened #9.

1. Let _k_ be 0.
1. Repeat,
1. If _k_ &ge; 2<sup>53</sup> - 1, then
Expand All @@ -77,8 +78,26 @@ <h1><ins>Array.fromAsync ( _asyncItems_ [ , _mapfn_ [ , _thisArg_ ] ] )</ins></h
1. Let _defineStatus_ be CreateDataPropertyOrThrow(_A_, _Pk_, _mappedValue_).
1. If _defineStatus_ is an abrupt completion, return ? AsyncIteratorClose(_iteratorRecord_, _defineStatus_).
1. Set _k_ to _k_ + 1.
1. Perform AsyncFunctionStart(promiseCapability, _asyncSpreadClosure_).
1. Return Completion { [[Type]]: ~return~, [[Value]]: _promiseCapability_.[[Promise]], [[Target]]: ~empty~ }.
1. NOTE: _items_ is not an AsyncIterable or Iterable so assume it is an array-like object.
1. Let _arrayLike_ be ! ToObject(_items_).
1. Let _len_ be ? LengthOfArrayLike(_arrayLike_).
1. If IsConstructor(_C_) is *true*, then
1. Let _A_ be ? Construct(_C_, &laquo; 𝔽(_len_) &raquo;).
1. Else,
1. Let _A_ be ? ArrayCreate(_len_).
1. Let _k_ be 0.
1. Repeat, while _k_ &lt; _len_,
1. Let _Pk_ be ! ToString(𝔽(_k_)).
1. Let _kValue_ be ? Get(_arrayLike_, _Pk_).
1. If _mapping_ is *true*, then
1. Let _mappedValue_ be ? Call(_mapfn_, _thisArg_, &laquo; _kValue_, 𝔽(_k_) &raquo;).
1. Else, let _mappedValue_ be _kValue_.
1. Perform ? CreateDataPropertyOrThrow(_A_, _Pk_, _mappedValue_).
1. Set _k_ to _k_ + 1.
1. Perform ? Set(_A_, *"length"*, 𝔽(_len_), *true*).
1. Return _A_.
1. Perform AsyncFunctionStart(promiseCapability, _fromAsyncClosure_).
1. Return Completion { [[Type]]: ~return~, [[Value]]: _promiseCapability_.[[Promise]], [[Target]]: ~empty~ }.
</emu-alg>
<emu-note>
<p>The `fromAsync` function is an intentionally generic factory method; it does not require that its *this* value be the Array constructor. Therefore it can be transferred to or inherited by any other constructors that may be called with a single numeric argument.</p>
Expand Down

0 comments on commit 64a4921

Please sign in to comment.