dynamic import() polyfill + question
Matthew Robb
matthewwrobb at gmail.com
Sat Apr 22 15:27:21 UTC 2017
I think that if an export is statically async that the promise should hold
up resolving that module as ready. When resolving the current dependency
graph.
On Apr 22, 2017 10:50 AM, "Andrea Giammarchi" <andrea.giammarchi at gmail.com>
wrote:
> Cyril the discussion is now rather about about asynchronous export.
>
> However, what you linked is not a polyfill for import, that's more like a
> half backed require.
>
> The polyfill for dynamic import where you actually use `import(path)` as
> specified on stage 3 is here:
> https://github.com/WebReflection/import.js
>
> The universal attempt to add `.import()` as CommonJS module is here:
> https://github.com/WebReflection/common-js
>
> Latter does what you wrote but on both client and server (it also probably
> resolves relative paths in a slightly different (more accurate?) way.
>
> That pattern never convinced CommonJS chaps that believes nobody wants
> asynchronous requires in this world (I actually do as much as I want
> asynchronous exports too ^_^)
>
> Regards
>
>
>
> On Sat, Apr 22, 2017 at 3:41 PM, Cyril Auburtin <cyril.auburtin at gmail.com>
> wrote:
>
>> If the discussion is about a polyfill for import()
>> <https://github.com/tc39/proposal-dynamic-import> (not the static import)
>>
>> it's not too hard: https://gist.github.com/caub/4
>> 58cfe944f8abcf7b1aec608d0a878cc
>>
>> ```js
>> (async()=>{
>> const [Stuff, {foo, bar}] = await Promise.all(['./x',
>> './y'].map(require));
>> // ..
>> })()
>>
>>
>> ```
>>
>> 2017-04-22 16:31 GMT+02:00 Andrea Giammarchi <andrea.giammarchi at gmail.com
>> >:
>>
>>> > Why not just allow `export await` in all export syntactic forms?
>>>
>>> that would work for me, and it would be like my initial, and second,
>>> example: `export default await Promise.all(...).then(...)`
>>>
>>> however, just to better understand what you're up to, I wonder if the
>>> module would be held, in a non blocking way, until all asynchronous exports
>>> have been resolved (desired) as opposite of introducing complexity for
>>> hybrid modules where you have to await everything to be sure it won't break
>>> (shenanigans)
>>>
>>> TL;DR unless the following would be possible too, please consider making
>>> modules available only once fully resolved through their exports
>>>
>>> ```js
>>> import await * as module from './module.js';
>>> ```
>>>
>>> Regards
>>>
>>>
>>>
>>>
>>> On Sat, Apr 22, 2017 at 3:08 PM, Matthew Robb <matthewwrobb at gmail.com>
>>> wrote:
>>>
>>>> I know you probably didn't want things to go this direction in the
>>>> conversation but this made me think up a generic way to do this. Why not
>>>> just allow `export await` in all export syntactic forms? This would not be
>>>> the same as top level await but a signal that the export will be the result
>>>> of an asynchronous operation that follows the await.
>>>>
>>>> Then you could potentially do `export default await (async ()=>{
>>>>
>>>> })();`
>>>>
>>>> On Apr 21, 2017 3:10 PM, "Bradley Meck" <bradley.meck at gmail.com> wrote:
>>>>
>>>> > how's that different from a Promise ?
>>>>
>>>> `later` is not const and could change over time. Could even be set via
>>>> something like:
>>>>
>>>> ```
>>>> setInterval(() => later = Date.now(), 1e3);
>>>> ```
>>>>
>>>> On Fri, Apr 21, 2017 at 2:00 PM, Andrea Giammarchi <
>>>> andrea.giammarchi at gmail.com> wrote:
>>>>
>>>>> > let later;
>>>>> > export default {then(notify) { if (ready) notify(later); else
>>>>> queue(notify); }}
>>>>>
>>>>> how's that different from a Promise ?
>>>>>
>>>>> Don't get me wrong, I have a module [1] that does that already (use a
>>>>> symbol as key and that's it) but yet for an importer, if that has to be
>>>>> handled like a Promise, then why not just a Promise ?
>>>>>
>>>>> This is the bit I don't get.
>>>>>
>>>>> Thanks
>>>>>
>>>>> [1] https://github.com/WebReflection/broadcast#broadcast--
>>>>>
>>>>>
>>>>>
>>>>> On Fri, Apr 21, 2017 at 7:38 PM, Bradley Meck <bradley.meck at gmail.com>
>>>>> wrote:
>>>>>
>>>>>> > how asynchronous export helps here ?
>>>>>>
>>>>>> >>> I cannot think about a single use case for wanting that: it's
>>>>>> not usable from within the module, it won't be usable outside unless
>>>>>> checked via ... an interval ?
>>>>>>
>>>>>> As stated in previous email:
>>>>>>
>>>>>> >> The previous email was stating there are use cases for updating
>>>>>> exports.
>>>>>>
>>>>>> If you are updating exports that in general means live bindings /
>>>>>> asynchronous work.
>>>>>>
>>>>>> > already covered by `export default new Promise(async () => {})` ,
>>>>>> right ?
>>>>>>
>>>>>> Kind of, this sacrifices live binding since `default` can only ever
>>>>>> have 1 value. Something could use a thenable to export multiple values over
>>>>>> time however similar to a live binding:
>>>>>>
>>>>>> ```
>>>>>> let later;
>>>>>> export default {then(notify) { if (ready) notify(later); else
>>>>>> queue(notify); }}
>>>>>> ```
>>>>>>
>>>>>> > how is the module consumer supposed to know when these exports are
>>>>>> ready?
>>>>>>
>>>>>> > if it's an event emitted, libraries trusting the event that already
>>>>>> happened will never know, so we are back to polling, which is a very bad
>>>>>> approach, IMO, and if the solution is a Promise then it's covered already.
>>>>>>
>>>>>> Please read my previous email:
>>>>>>
>>>>>> >> The answer is no pattern has been standardized so it depends on
>>>>>> your proposed solution. A `then()`-able is already in spec and seems like a
>>>>>> possible choice (though I wouldn't use a Promise); top level await could be
>>>>>> another but blocks the module graph. TDZ poll/checking on imports could be
>>>>>> another (though not-preferable) solution. I am sure we could bikeshed other
>>>>>> approaches.
>>>>>>
>>>>>> > so if two importers happen at different times the second importer
>>>>>> can compromise with undesired features the first one or vice-verssa?
>>>>>>
>>>>>> No, ESM modules are only evaluated once. Such checks are most likely
>>>>>> done up front. However, enabling a debugger for example might cause a new
>>>>>> set of exports to be loaded/exported.
>>>>>>
>>>>>> > So, like I've said, I don't see real-world scenarios for exported
>>>>>> modules that changes without notice.
>>>>>> It looks unpractical and undesired.
>>>>>>
>>>>>> As stated in previous email:
>>>>>>
>>>>>> > Exporting asynchronously doesn't provide any coordination point ...
>>>>>>
>>>>>> The rest of my email(s) have been talking about coordination.
>>>>>>
>>>>>> On Fri, Apr 21, 2017 at 1:28 PM, Andrea Giammarchi <
>>>>>> andrea.giammarchi at gmail.com> wrote:
>>>>>>
>>>>>>> > It could be something that is being mocked/spied upon.
>>>>>>>
>>>>>>> how asynchronous export helps here ?
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> > It could be part of a circular dependency and so the modules do
>>>>>>> get a hold of eachother without finishing evaluation.
>>>>>>>
>>>>>>> already covered by `export default new Promise(async () => {})` ,
>>>>>>> right ?
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> > It could be that it lazily/async populates its exports due to
>>>>>>> costs.
>>>>>>>
>>>>>>> how is the module consumer supposed to know when these exports are
>>>>>>> ready?
>>>>>>>
>>>>>>> if it's an event emitted, libraries trusting the event that already
>>>>>>> happened will never know, so we are back to polling, which is a very bad
>>>>>>> approach, IMO, and if the solution is a Promise then it's covered already.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> > It could be that it is relying upon context to determine if
>>>>>>> something should be exported (debug flag etc.)
>>>>>>>
>>>>>>> so if two importers happen at different times the second importer
>>>>>>> can compromise with undesired features the first one or vice-verssa?
>>>>>>>
>>>>>>> Dynamic exports are possible since ever on CommonJS world (same as
>>>>>>> imports) and I've truly rarely seen the need to lazy export or lazy import.
>>>>>>> Conditional import yes, and conditional exports too but never at distance.
>>>>>>>
>>>>>>> So, like I've said, I don't see real-world scenarios for exported
>>>>>>> modules that changes without notice.
>>>>>>> It looks unpractical and undesired.
>>>>>>>
>>>>>>> Can you point at me at a single module that needs to do that?
>>>>>>> Maybe I'm missing something.
>>>>>>>
>>>>>>> Thanks
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Fri, Apr 21, 2017 at 7:18 PM, Bradley Meck <
>>>>>>> bradley.meck at gmail.com> wrote:
>>>>>>>
>>>>>>>> > Let me ask again: as a module consumer, how are you supposed to
>>>>>>>> know when an export is available?
>>>>>>>>
>>>>>>>> The previous email was stating there are use cases for updating
>>>>>>>> exports.
>>>>>>>>
>>>>>>>> The answer is no pattern has been standardized so it depends on
>>>>>>>> your proposed solution. A `then()`-able is already in spec and seems like a
>>>>>>>> possible choice (though I wouldn't use a Promise); top level await could be
>>>>>>>> another but blocks the module graph. TDZ poll/checking on imports could be
>>>>>>>> another (though not-preferable) solution. I am sure we could bikeshed other
>>>>>>>> approaches.
>>>>>>>>
>>>>>>>> I don't see "None of these haven't been solved already through
>>>>>>>> better pattern." as a response to all the use cases I described.
>>>>>>>>
>>>>>>>> On Fri, Apr 21, 2017 at 1:13 PM, Andrea Giammarchi <
>>>>>>>> andrea.giammarchi at gmail.com> wrote:
>>>>>>>>
>>>>>>>>> None of these haven't been solved already through better pattern.
>>>>>>>>>
>>>>>>>>> Let me ask again: as a module consumer, how are you supposed to
>>>>>>>>> know when an export is available?
>>>>>>>>>
>>>>>>>>> On Fri, Apr 21, 2017 at 7:08 PM, Bradley Meck <
>>>>>>>>> bradley.meck at gmail.com> wrote:
>>>>>>>>>
>>>>>>>>>> Could be several reasons, it could be exporting a counter/log
>>>>>>>>>> that changes over time.
>>>>>>>>>>
>>>>>>>>>> It could be something that is being mocked/spied upon.
>>>>>>>>>>
>>>>>>>>>> It could be part of a circular dependency and so the modules do
>>>>>>>>>> get a hold of eachother without finishing evaluation.
>>>>>>>>>>
>>>>>>>>>> It could be that it lazily/async populates its exports due to
>>>>>>>>>> costs.
>>>>>>>>>>
>>>>>>>>>> It could be that it is relying upon context to determine if
>>>>>>>>>> something should be exported (debug flag etc.)
>>>>>>>>>>
>>>>>>>>>> Probably plenty more reasons.
>>>>>>>>>>
>>>>>>>>>> On Fri, Apr 21, 2017 at 11:58 AM, Andrea Giammarchi <
>>>>>>>>>> andrea.giammarchi at gmail.com> wrote:
>>>>>>>>>>
>>>>>>>>>>> > a Promise cannot change value over time, unlike a live
>>>>>>>>>>> binding.
>>>>>>>>>>>
>>>>>>>>>>> when is a module that changes values and without any
>>>>>>>>>>> notification desirable?
>>>>>>>>>>>
>>>>>>>>>>> I cannot think about a single use case for wanting that: it's
>>>>>>>>>>> not usable from within the module, it won't be usable outside unless
>>>>>>>>>>> checked via ... an interval ?
>>>>>>>>>>>
>>>>>>>>>>> The main point here is that asynchronous import might also
>>>>>>>>>>> inevitably mean asynchronous exports.
>>>>>>>>>>>
>>>>>>>>>>> Early access to unusable modules doesn't seem a real-world
>>>>>>>>>>> solution to me.
>>>>>>>>>>>
>>>>>>>>>>> What am I missing?
>>>>>>>>>>>
>>>>>>>>>>> Best Regards
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On Fri, Apr 21, 2017 at 5:48 PM, Bradley Meck <
>>>>>>>>>>> bradley.meck at gmail.com> wrote:
>>>>>>>>>>>
>>>>>>>>>>>> I have been thinking about this some, I do think there is
>>>>>>>>>>>> something here, but am not sure it warrants any changes. Exporting
>>>>>>>>>>>> asynchronously doesn't provide any coordination point so the general idea
>>>>>>>>>>>> is to export a Promise, but a Promise cannot change value over time, unlike
>>>>>>>>>>>> a live binding. So, a more appropriate way might be to export a "ready"
>>>>>>>>>>>> binding that is a Promise. Without some kind of async coordination like a
>>>>>>>>>>>> `.then()`-able you would also suffer from `undefined` being a possible
>>>>>>>>>>>> initialized and uninitialized value.
>>>>>>>>>>>>
>>>>>>>>>>>> ```
>>>>>>>>>>>> let later;
>>>>>>>>>>>> export {later};
>>>>>>>>>>>> export const ready = someAsyncWork().then(v => later = v);
>>>>>>>>>>>> ```
>>>>>>>>>>>>
>>>>>>>>>>>> This does still mean that `later` can be accessed before it is
>>>>>>>>>>>> ready, in my opinion somewhat against the idea of a TDZ wanting to wait for
>>>>>>>>>>>> access to be ready.
>>>>>>>>>>>>
>>>>>>>>>>>> I would be interested in something like:
>>>>>>>>>>>>
>>>>>>>>>>>> ```
>>>>>>>>>>>> async let later;
>>>>>>>>>>>> export {later};
>>>>>>>>>>>> export const ready = someAsyncWork().then(v => later = v);
>>>>>>>>>>>> ```
>>>>>>>>>>>>
>>>>>>>>>>>> That preserves the TDZ until assignment. Or, something that
>>>>>>>>>>>> wraps `later` in a non-promise `.then()`-able that `import` understands and
>>>>>>>>>>>> can unwrap to a live binding.
>>>>>>>>>>>>
>>>>>>>>>>>> All of that said, I am not sure this specific of a use warrants
>>>>>>>>>>>> language changes as I can think of problems with the ideas I have proposed
>>>>>>>>>>>> as well.
>>>>>>>>>>>>
>>>>>>>>>>>> On Fri, Apr 21, 2017 at 11:24 AM, Benoit Marchant <
>>>>>>>>>>>> marchant at mac.com> wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>> I really like that idea
>>>>>>>>>>>>>
>>>>>>>>>>>>> On Apr 21, 2017, at 08:22, Andrea Giammarchi <
>>>>>>>>>>>>> andrea.giammarchi at gmail.com> wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>> nobody has any thought on this ?
>>>>>>>>>>>>>
>>>>>>>>>>>>> Maybe the following pattern would be just about enough to
>>>>>>>>>>>>> solve a generic asynchronous import/export ?
>>>>>>>>>>>>>
>>>>>>>>>>>>> ```js
>>>>>>>>>>>>> export default new Promise(async $export => {
>>>>>>>>>>>>>
>>>>>>>>>>>>> const utils = await import('./utils.js').default;
>>>>>>>>>>>>>
>>>>>>>>>>>>> $export({module: 'asynchronous', utils});
>>>>>>>>>>>>>
>>>>>>>>>>>>> });
>>>>>>>>>>>>> ```
>>>>>>>>>>>>>
>>>>>>>>>>>>> Best Regards
>>>>>>>>>>>>>
>>>>>>>>>>>>> On Thu, Apr 20, 2017 at 11:51 AM, Andrea Giammarchi <
>>>>>>>>>>>>> andrea.giammarchi at gmail.com> wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> Even if unpolyfillable through simple `function import() {}`
>>>>>>>>>>>>>> declaration,
>>>>>>>>>>>>>> I've managed to create a polyfill/payground for the ESnext's
>>>>>>>>>>>>>> dynamic import() [1]
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> This also made me wonder if there's any plan to provide a way
>>>>>>>>>>>>>> to asynchronously
>>>>>>>>>>>>>> export modules that depends on those that use asynchronous
>>>>>>>>>>>>>> import.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Since AFAIK modules have no top-level await, the only pattern
>>>>>>>>>>>>>> I can see right now
>>>>>>>>>>>>>> to import something asynchronous is the following one:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> ```js
>>>>>>>>>>>>>> // module ./js/c.js
>>>>>>>>>>>>>> export default Promise.all([
>>>>>>>>>>>>>> import('./js/a.js'),
>>>>>>>>>>>>>> import('./js/a.js')
>>>>>>>>>>>>>> ]).then([a, b] => {
>>>>>>>>>>>>>> const module = {a, b, c() {}};
>>>>>>>>>>>>>> return module;
>>>>>>>>>>>>>> });
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> // module that uses ./js/c.js
>>>>>>>>>>>>>> import('./js/c.js').then(m => m.default).then(c => {
>>>>>>>>>>>>>> c.a(); c.b(); c.c();
>>>>>>>>>>>>>> });
>>>>>>>>>>>>>> ```
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> However, above boilerplate doesn't seem ideal compared with
>>>>>>>>>>>>>> something like the following:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> ```js
>>>>>>>>>>>>>> // module ./js/c.js
>>>>>>>>>>>>>> export default await Promise.all([
>>>>>>>>>>>>>> import('./js/a.js'),
>>>>>>>>>>>>>> import('./js/a.js')
>>>>>>>>>>>>>> ]).then([a, b] => {
>>>>>>>>>>>>>> const module = {a, b, c() {}};
>>>>>>>>>>>>>> return module;
>>>>>>>>>>>>>> });
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> // module that uses ./js/c.js
>>>>>>>>>>>>>> import * as c from './js/c.js';
>>>>>>>>>>>>>> ```
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> But again, AFAIK that's not possible.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> The clear advantage is that the module consumer wouldn't need
>>>>>>>>>>>>>> to know, or care,
>>>>>>>>>>>>>> if the loaded module depends on some dynamic, asynchronous,
>>>>>>>>>>>>>> import,
>>>>>>>>>>>>>> meaning modules can be updated and eventually moved to async
>>>>>>>>>>>>>> transparently
>>>>>>>>>>>>>> for any module consumer.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> As summary, is any solution worth
>>>>>>>>>>>>>> exploring/improving/fixing/planning?
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Thank you.
>>>>>>>>>>>>>> Best Regards
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> [1] https://github.com/WebReflection/import.js#importjs
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> _______________________________________________
>>>>>>>>>>>>> es-discuss mailing list
>>>>>>>>>>>>> es-discuss at mozilla.org
>>>>>>>>>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> _______________________________________________
>>>>>>>>>>>>> es-discuss mailing list
>>>>>>>>>>>>> es-discuss at mozilla.org
>>>>>>>>>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> es-discuss at mozilla.org
>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>
>>>>
>>>>
>>>
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>>>
>>>
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>>
>>
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170422/1babadb8/attachment-0001.html>
More information about the es-discuss
mailing list