-
-
Notifications
You must be signed in to change notification settings - Fork 591
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
[dynamic-import-vars] Give default promise rejection timing parity with cases #825
Conversation
044210c
to
7652817
Compare
7652817
to
6d07141
Compare
@LarsDenBakker it looks like this has some community support. Please have a look over this when you have the opportunity. |
This changes the natural behavior of dynamic imports, I'm not sure if it's a good idea. For example, given: import(`./pages/${page}.js`); If the requested file does not exist, the browser will throw an error before bundling with rollup. Why should it not throw an error after bundling with rollup? If your code is intended to ignore/handle 404s, you can do this: try {
await import(`./pages/${page}.js`);
} catch (error) {
// handle the error
} That way the code behaves the same both before and after bundling. |
Perhaps it should (optionally) throw an error after bundling, but if it does, it should do so asynchronously, as the 404 error would be. The reason it would be nice to be optional is that in the actual code that is run after bundling, there is no natural error. Nothing has been attempted and nothing has failed. If you loop through a set of variables until one matches, as we do (and one is guaranteed to match), it would be nice not to have to catch a pointless error. Also note that your example, as written, will never |
You're right I'm missing an Improving the way we handle the rejected promise is fine by me, I wasn't aware we didn't achieve this already with the current code. I'm personally still not convinced of the use case you describe. If there is high demand for it, we can add it. But as far as I can see it's a few lines of code to handle the catch yourself and it makes the code more explicit and work without bundling as well... |
I think that's fair. If we queue the promise rejection as a microtask, making the default clause optional becomes less necessary, and it can be handled in a Should I go ahead and revert the |
Sure, that would be fine! |
throw new Error('Unknown variable dynamic import: ' + path); | ||
return new Promise((resolve, reject) => { | ||
queueMicrotask(() => { | ||
reject(new Error("Unknown variable dynamic import: " + path)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry not reviewed this in detail, but wondering if queueMicrotask
is supported in most places or if we should use something a bit older instead to achieve this?
Would a standard return Promise.reject()
not work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would not. The actual code falls back to setTimeout
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looked in a bit more detail.
What wouldn’t work with return Promise.reject(new Error("Unknown variable dynamic import: " + path))
work?
The return is always a promise then which would resolve/reject async. I understand why the sync throw before was not great
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code actually already is using Promise.reject
. The documentation just doesn't match. Please run this with your dev tools open and Pause on exception
enabled to see what this fixes.
https://jsfiddle.net/g6u4femL/
Note that even though both promises are caught, it will still break on promiseA
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok thanks for explaining.
Seems strange there’s not a simpler way but 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels like this is something dev tools should handle? If you’re pausing on uncaught exceptions I wonder why it doesn’t wait a tick internally until the point when it actually rejects 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense to me, but I think we could just return a Promise.reject(...)
and solve the problem with less code, so that would be preferable IMO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry just spotted something else
} | ||
switch (path) { | ||
${paths.map((p) => ` case '${p}': return import('${p}');`).join('\n')} | ||
${` default: return new Promise((resolve, reject) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use a standard function in here vs the arrow one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose an arrow function isn't strictly necessary. Do you see a downside to it in this case? There's no scope it's ignoring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It depends what browsers this is targeting. Promises are polyfillable but the arrow syntax isn’t, so it would be safer to stick with the older function format I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok. Didn't realize we were going back that far with browser support, but yeah I guess it's the safest thing to do and no real downside.
Anything blocking this? |
Looks good to go but @shellscape usually does the merge/release when they get chance :) |
@tjenkinson put your green checkmark on it and I'll merge it post haste |
Rollup Plugin Name:
dynamic-import-vars
This PR contains:
Are tests included?
Breaking Changes?
If yes, then include "BREAKING CHANGES:" in the first commit message body, followed by a description of what is breaking.
List any relevant issue numbers:
Description
The immediately rejected promise in the
default
clause of the generatedswitch
does not have timing parity with the other clauses, making it impossible to register acatch
on the promise before it is rejected, which causes debuggers with "pause on exceptions" (but not "pause on caught exceptions") enabled to always pause in thedefault
clause, even if you have registered acatch
on the returned promise. This queues the promise rejection as a microtask to remedy that situation.