dynamic import() polyfill + question
Andrea Giammarchi
andrea.giammarchi at gmail.com
Fri Apr 21 19:00:51 UTC 2017
> 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/pla
>>>>>>>>> nning?
>>>>>>>>>
>>>>>>>>> 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
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170421/eeaed0b6/attachment-0001.html>
More information about the es-discuss
mailing list