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

Request for clarification on AsyncIterator behavior #765

Closed
michaelsbradleyjr opened this issue Feb 5, 2020 · 4 comments
Closed

Request for clarification on AsyncIterator behavior #765

michaelsbradleyjr opened this issue Feb 5, 2020 · 4 comments
Labels

Comments

@michaelsbradleyjr
Copy link

michaelsbradleyjr commented Feb 5, 2020

The code below illustrates a difference in behavior between core-js AsyncIterator, the equivalent facility in IxJS, and a for await...of analog.

require('core-js/proposals/iterator-helpers');

const ps = [Promise.resolve(1), 2, Promise.resolve(3)];

(async () => {
  const arr = [];
  await AsyncIterator.from(ps).forEach(v => arr.push(v));
  console.log(arr) // => [ Promise{ 1, ... }, 2, Promise{ 3, ... } ]
})();
const { from: ixFrom } = require('ix/asynciterable');

const ps = [Promise.resolve(1), 2, Promise.resolve(3)];

(async () => {
  const arr = [];
  await ixFrom(ps).forEach(v => arr.push(v));
  console.log(arr) // => [ 1, 2, 3 ]
})();
const ps = [Promise.resolve(1), 2, Promise.resolve(3)];

(async () => {
  const arr = [];
  for await (const v of ps) {
    arr.push(v)
  }
  console.log(arr) // => [ 1, 2, 3 ]
})();

My question: is the difference per spec and deliberate?

The behavior of IxJS seems preferable because it allows the possibility of processing arrays of promises (and non-promises, or mixed) one may build up without resorting to an additional await (e.g. in the callback to forEach).

When iterating with for await (const x of xs) {} the behavior is the same whether xs is an actual array or an object that has the [Symbol.asyncIterator] or [Symbol.iterator] properties.

@michaelsbradleyjr
Copy link
Author

michaelsbradleyjr commented Feb 7, 2020

I have another request for clarification, but it's so closely related that I'm putting it in a comment on the existing issue.

The Iterator Helper docs in the README have:

class AsyncIterator {
  static from(iterable: Iterable<mixed>): AsyncIterator<any>;
  asIndexedPairs(): AsyncIterator<[index, any]>;
  drop(limit: uint): AsyncIterator<any>;
  every(async callbackfn: value: any => boolean): Promise<boolean>;
  filter(async callbackfn: value: any => boolean): AsyncIterator<any>;
  find(async callbackfn: value: any => boolean)): Promise<any>;
  flatMap(async callbackfn: value => any: Iterable): AsyncIterator<any>;
  forEach(async callbackfn: value => void): Promise<void>;
  map(async callbackfn: value => any): AsyncIterator<any>;
  reduce(async callbackfn: (memo: any, value: any) => any, initialValue: any): Promise<any>;
  some(async callbackfn: value: any => boolean): Promise<boolean>;
  take(limit: uint): AsyncIterator<any>;
  toArray(): Promise<Array>;
  @@toStringTag: 'AsyncIterator'
}

But try pasting the following example in a Node.js REPL:

setup

$ mkdir -p ~/temp/tryme && cd ~/temp/tryme
$ npm init -y && npm i core-js-pure && node

REPL

const AsyncIterator = require('core-js-pure/features/async-iterator');

const timer = (ms) => new Promise(resolve => { setTimeout(resolve, ms); });

const timedReturn = async (n) => { await timer(n); return n; };

const times = [300, 22, 1, 888, 999, 100, 2, 0];

const doubleCheck = [];
const tripleCheck = [];

const finished = (async () => {
  return AsyncIterator.from(times)
    .filter(n => !(n % 2))
    .map(n => { doubleCheck.push(n); return n; })
    .map(n => timedReturn(n))
    .map(n => { tripleCheck.push(n); return n; })
    .toArray();
})();

(async () => {
  console.log(await finished);
  console.log(doubleCheck);
  console.log(tripleCheck);
})();

// prints:
// [ 300, 22, 888, 100, 2, 0 ]
// [ 300, 22, 888, 100, 2, 0 ]
// [ 300, 22, 888, 100, 2, 0 ]

NOTE: none of the callback functions are async and the value passed to the callback is always resolved. But the use of async callbackfn in the docs may suggest to users that they need to await value when a previous callback returns a promise.

The Examples section does demonstrate that callbacks need not be async functions, but none show a callback returning a promise, so they don't entirely clear up the potential confusion.

@zloirock
Copy link
Owner

zloirock commented Nov 5, 2020

My question: is the difference per spec and deliberate?

core-js follows the spec here.

@zloirock
Copy link
Owner

zloirock commented Nov 5, 2020

About your second issue - it's rather related to the language used in the documentation which is non-standard. At this moment, we have no universal interface definition language for documentation in JS.

In this case, async means that the result of this callback will be wrapped to Promise and the method will wait when this promise will be resolved.

If you have some ideas on how to improve the documentation - feel free to propose your ideas or add a PR.

@zloirock
Copy link
Owner

It's fixed in current versions of core-js and proposal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants