-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Async Iterator Helpers on Async Generators #1366
Comments
For context, here is some history: Many years ago, during my career as a Java engineer, I had the opportunity to welcome the benefits of a lazy pull streams library when Java Streams was released as part of the Java Standard Library. Later, after working for some time using Node.js, I missed Java Streams, and in 2000, not being aware of the Iterator Helpers proposal or your polyfill, which was released in the fall of 2019, I endeavored to quickly prototype a basic lazy pull streams library for JavaScript (and maybe overly ambitiously, called it Since Iterator Helpers are here now, my goal is to produce a library that augments (Async) Iterator Helpers with additional sources and operators in a way seamlessly integrates into Iterator and AsyncIterator abstract classes/constructors from the proposal. I already have implementations of a few additions in the library as I mentioned, and these are only lacking some unit tests and documentation. But before I even focus on augmenting Iterator Helpers, I wanted to make sure Iterator Helpers in their current form (as implementations in the latest Node.js and browsers, and as core-js polyfills of the Async versions) are as easy to use as that streams library prototype would be on its own, namely:
Type DefinitionsOn the type definitions front, TypeScript just introduced the Iterator abstract class (and fixed the type definitions of functions that return built-in iterators to be able to support augmenting the Iterator and AsyncIterator classes without polluting the core next/return/throw iterator and async iterator protocols) in 5.6.2 released just last week, and an AsyncIterator abstract class type definition can be derived fairly trivially from the Iterator version. I already have done it and am thinking of attempting to push that to DefinitelyTyped at Polyfills Backwards Compatibility and Future-proofingIteratorI have come to the conclusion that in the case of Iterator, all outlined requirements are met with the current state of core-js and babel and TypeScript type definitions. I can also reliably retrieve a reference to an Iterator abstract class (from the runtime or core-js) and fall-back to polyfilling it myself, before augmenting it with additional capabilities. Note: Unlike core-js, which is restricted from using modern syntax, a library providing additional iterator helpers would be best written using modern syntax and then be transpiled for older runtimes if needed by its clients in their app bundling phase. Would you agree? Side noteOne goal of mine with the streams library would be to provide a template for others who want to write additional iterator helpers to be able to do so with ease, and in a way that is also easy to consume in all possible environments. There may be value in that - rxjs has something like 120 operators and if I understand correctly, they maybe had even more in earlier versions, and tried to move away from being a kitchen-sink library... now granted, some of those do not seem applicable to pull streams, but still - it would be nice if it was easy to inject operators in Iterator and AsyncIterator and publish those as a library and have a clear path for consuming those operators in all runtimes via at least the babel +corejs (+webpack?) pipeline. AsyncIteratorAsyncIterator is a bit more challenging (and at the same time simpler since there are no built-in iterators that predated generators, but those complexities I have gladly discovered are solved in a very robust manner by core-js today - Kudos for that @zloirock this is hard work and core-js makes very difficult problems go away in a very through comprehensive manner! Thank you! And you can quote me on that). This where my question comes from that I put out to you via this github issues channel. |
I tried the above example under ESM and discovered another caveat! ESM loads and initializes all imported modules before executing any code, and so both places in the example have to use dynamic imports ( |
Maybe these are not real-world scenarios, but I am also discovering that babel does not seem to transpile ESM
|
... I am starting to wonder how deno will behave in this context... |
So the solution to the ESM non-dynamic import problem is to pull the block of code into its own file. This is something to be done from the entry point of individual applications. An npm package could be published that can be simply imported and then included in transpilation. I really hoped this could be done somehow as part of core-js or a combination of core-js and babel utilities, which is why I opened this issue here. However, this does not seem to be the case and I guess that is okay. I have published two gists with the code in CJS and ESM form for easy adoption in projects via copy-paste: https://gist.github.com/nikolaybotev/acab33b7387e4773bc1749f1c556efbb and https://gist.github.com/nikolaybotev/56e916666c214e0cf501672414e90b1d |
There is a specific babel configuration needed to make this work as well, in terms of ensuring runtime helpers are installed only once on the entire bundle to ensure a single |
There is now a commonjs version of the sample as well in te commonjs branch here: https://github.com/nikolaybotev/async-iterator-helpers-demo/tree/commonjs |
Hi @zloirock,
I have been thinking hard about what the best way is to make Async Iterator Helpers (through your polyfills) as widely available in an as easy manner as possible.
Just for context, as you describe in the README, the problem is that unlike iterator helpers, for async iterator helpers it is not possible to retrieve a reference to the (currently hidden) async iterator prototype object (without using modern syntax, which core-js is constrained from using). So the following does not work out of the box:
On modern runtimes with async generator syntax, this can be fixed by leveraging the configurator, with the following snippet at the top of the script:
On older runtimes, when transpiling using babel's standard preset-env, the above does not work, because the transpiler does not install the full standard prototype chain on async generator objects. Luckily, this can be worked around fairly simply, like so:
Also, it should not be too difficult to fix the transpiler to provide the proper prototype chain, especially since this has already been done for regular generators in the regenerator library, whose approach can be adopted for async generators. Then the above setup code makes async iterator helpers readily available on async generators in all environments. It is also future-proof, in that when async iterator helpers become implemented by runtimes, the native AsyncIterator constructor will be used.
My question then is this: do you think it is reasonable to add a babel plugin (say
proposal-async-iterator-helpers
, which installs the above snippet in code during transpilation, and runs before the async generator function transformer)? Or do you think there is a better approach?Thanks for the attention.
The text was updated successfully, but these errors were encountered: