-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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
worker: Add experimental SynchronousWorker implementation #45018
Conversation
Review requested:
|
6bd7eec
to
5058f78
Compare
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.
LGTM
There is a tough bug in the original code.. but having it in core will allow us to actually fix them.
72cee12
to
47e2483
Compare
This comment was marked as outdated.
This comment was marked as outdated.
609b29a
to
8d6488a
Compare
@mcollina are you referring to addaleax/synchronous-worker#8? |
Yes indeed. |
46e0246
to
ba13228
Compare
This comment was marked as outdated.
This comment was marked as outdated.
Is there an explanation for this module/feature to be maintained in the core rather than a userland module? Is it possible for us to incorporate the patches mentioned in addaleax/synchronous-worker#8 into the core? |
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 would like to land this feature, but before we add it I think it needs a few things:
- It needs to work with built-in
fetch
. The primary example of the API usesfetch
(albeitnode-fetch
because internal doesn’t work for some reason) and I can see lots of users trying to use this with ourfetch
and thinking that something is broken (which it is). - We should update the docs to replace
node-fetch
with ourfetch
. - We need ESM versions of the examples in the docs.
- The feature itself needs to work in ESM, and have tests proving that it does. (I’m assuming that it does already, but without tests I’m unsure.) We don’t need a copy of the full 265-line test file ported into ESM, but at least core functionality should be in both module systems since this seems to be a feature that might be module system-dependent. We might want to consider making the primary test file in ESM and have a CommonJS version include just core/basic functionality and functionality that might differ in CommonJS.
Along the lines of the above, I can see a huge number of people using this feature as a way to synchronously import ESM packages into CommonJS; like the folks frustrated that the latest, ESM-only version of node-fetch
doesn’t work in CommonJS without dynamic import()
. We should consider what this would mean: would such an outcome be bad? (I feel like it would, but why, exactly?) Should we mention it in the documentation, either with a recommendation or with a warning (like to discourage its use)? If we encourage the pattern, we should probably have tests around it.
I understand some folks might respond to this to-do list with “but it’s experimental! Just land it and we’ll iterate!” but I feel like there could be some big unintended consequences to enabling the syncification of async code, and we need to be a bit wary before putting such an ability into core. Again I’m 100% on board with doing so, and I can be persuaded that some of my suggestions above are unnecessary, but I think we should be careful with this one.
I definitely agree that those are things that should happen before it comes out of experimental but disagree that they should block this landing initially being the experimental flag. We've allowed multiple experimental features to land while still being incomplete. I wanted to keep this initial pr focused first on getting the basic functionality added and spend subsequent PRs iterating. The fetch issue, for instance, is one that I would rather work on in a separate PR as a distinct piece of work.
That's a fundamentally different concern. Let's not use an unrelated set of TODO items as an excuse to block when the issue is the concern over adding deasync functionality in general. If that's where the concern lies, then let's discuss that specifically |
@jasnell @mcollina My concern is specifically about the constructor options invalidating the core pattern When I would love to land this feature to explore the ability to run asynchronous tasks synchronously, but I would also like to distinguish those distinct use cases. To be clear, I'm totally onboarded with those needs. For creating disposable node environments, I would find it the goal of the environments and realms refactoring to expose a full set of Node.js built-in modules in the |
Is there a reason to make this a worker_threads thing when it's not threading rather than adding a way to run a vm.Module synchronously? Seems to me like building this sort of behaviour into the vm module makes it a bit more flexible. 🤔 |
I’m not concerned about adding deasync functionality. As I wrote, I want to ship this feature. Most of my list were simply questions, that haven’t yet been answered. I’ll repeat them here:
This feature might be able to solve a pressing user desire: being able to import ESM-only packages like So let’s at least answer all the above questions. If we can’t fix all those issues in one PR, which I understand, perhaps we could require a build flag for the |
From my tests, it works.
It requires a tiny patch to support ESM. See mcollina/worker@5d38649.
Not at this time. As you can see in the commit above, you can use a commonjs file to dynamically import. It's definitely within the realm of the possibilities to add that functionality.
I do not have a good answer for this right now. I think it could be done with some limitations. The loaded module needs to have separate loops to remove the async nature, however for something like node-fetch to work it needs to run on the main event loop. In other terms, it's unlikely to work right now for complex use cases (like node-fetch). Long term, I believe this feature could enable us to have some custom synchronous
My primary use case of this feature is to have hot module reloading in a pure ESM context. Barred bugs, it's incredible DX.
I'm not sure it's possible. Every time I tried to fix any of the mentioned shortcomings I stumbled upon Node core code that I will have to change to make that possible. |
There is zero intent to exclude ESM support from this feature, but I propose that we defer answering these questions to a subsequent PR. This initial PR is not intended to address all of the bugs that may exist in this feature currently.
Yes.
I'll need you to be more specific with the first part of this question. createRequire() does work in ESM. I do not yet know if
As Matteo indicates, not currently. It creates a
Yes, but currently only indirectly and with limitations. For instance, in the following example from the tests, we use const w = new SynchronousWorker();
const req = w.createRequire(__filename);
const fixture = fixtures.path('es-modules', 'message.js');
const message = w.runLoopUntilPromiseResolved(req(fixture)());
strictEqual(message, 'A message'); This could likely be improved, yes, but the intent of this PR is not to make API improvements. My intent is for that to happen in subsequent PRs.
This PR does not seek to encourage or discourage anything for any reason. |
Unfortunately I don't know how to translate this into actionable changes. I understand what your concerns are but I do not know what concrete changes you are wanting to see made. Is it just renaming the class? Moving it off of |
I’m just going to respond to this by itself for now, since there are a lot of questions zooming around here right now. In short, I don’t think it’s okay to land a feature, even as experimental, that defers full ESM support until later. Not only may later never come, but as soon as a feature is accessible to users they’ll start opening bug reports against it, and it’s the entire team’s responsibility to address those. We incur a maintenance burden as soon as the public has access to a feature. I sympathize with the desire to split up the work into several PRs. I can think of a few ways to enable that while still leaving this feature inaccessible from the public:
|
I'm perfectly fine with don't land labels for the time being. I work with @mcollina on determining a path forward here. |
Could we change
The existing
The examples in the docs of this PR show it working with
If this is already working, can we get some tests added for this use case? And some tests for the basic functionality running in an ESM context, since I think you’re saying that that’s also already working. (This feels like something that could and should be part of this initial PR.) How do you feel about this list of requirements? For merging in at all, even with the
For removing the
|
-1 to changing I'm also -1 on adding an experimental warning unless we also get rid of the experimental flag. We should have one or the other, there's no reason to have both.
If @mcollina wants to provide such a test. I disagree that it should be a necessary requirement for this to land. It's a use of this API, yes, but it's not the only use of it, and we can demonstrate the api works without it. I also still definitely disagree that this initial PR must have full ESM capability demonstrated up front to land but, ok, I'll explore that if it helps unjam it. |
I’m fine with whatever solution that treats both module systems as first-class. This can also come later if it’s too big a change for this initial PR.
We have lots of experimental features with both a flag and a warning:
If there isn’t a test, unrelated changes may break this ability. For example, once #44710 lands will |
After talking it over with @mcollina ... we've decided to go a different direction. |
This feature is mostly self-contained, expand the capability of Node.js and if successful will solve real problems people have today. It's not a major feature of Node.js in any way, but rather something minor. The original module powers the CLI of MongoDB, and I've used it daily too. Given the production usage, it's ready enough to land with an experimental warning and no experimental flag. James and myself talked briefly about this after I demoed my hot-reloading-esm solution. He volunteered to open a PR to bring Anna's work in as-is. Finishing this will likely require a collaborative effort. What this PR is about is an ask to iterate on this in core and incrementally get there. I see a lot of LGTMs and only a significant request for changes which will require quite a significant amount of additional work to get complete. I cannot ask @jasnell to put much more time into brining this over the finishing line, and at the same time I do not want to call a @nodejs/tsc vote for something that is not ready yet. We are likely going to iterate on it and propose a different solution for the same problem. |
I chatted with @mcollina about this. The question of how much compatibility with ESM is reasonable to require of experimental features is one that I’m unaware of being discussed before, so I opened #45084 to try to get an answer on that. I’ll defer to whatever consensus that discussion arrives at with regard to that. I think my list in #45018 (comment) for what this PR needs in order to land is reasonable. This feature has no tests for functionality that supposedly already works in ESM; we don’t usually merge in PRs with incomplete tests, even for experimental features. This feature has no documentation of how to use it in ESM; I’m unaware of other features that are undocumented in ESM. For a feature with so many known issues, I think it’s reasonable to have a docs section spelling out those issues and other caveats so that users avoid frustration and don’t open lots of issues complaining about them. These are all very standard requests for PRs to be able to land. With regard to the second list, of what needs to be fixed in order for this to be released, I’m more flexible and I anticipate the list changing based on the state of this PR when it lands. If the feature is so broken that it doesn’t solve any user problems, it has little likelihood of becoming popular and therefore it doesn’t matter if it’s released or not. If it does solve the user problem of synchronously importing ESM libraries in CommonJS, then I think we need a higher-than-usual bar for releasing this, even as experimental. Especially since the original author is no longer involved, and @jasnell has limited time to work on this, this feels to me like a feature at unusually high risk of being merged in in an incomplete state and abandoned, or left without ESM parity. I want to avoid those outcomes; if we can find a way to do so that doesn’t involve using blocks, that would be my preference, because I do want to ship this feature. I feel like I’ve been direct about my concerns and I’m happy to work with this feature’s champions to get all issues addressed and to get this released. |
Let me be clear that this is not true at all.
This is also not what this is. There is no busy loop here.
Fixing that bug properly requires significant changes to libuv's event loop management code. I'd be super glad to see that happen but it's going to be a bunch of work for somebody to pick up.
It's probably a decent way to achieve this, but it's also a terrible sign if the |
I would like to make a personal point of view from an ordinary user's point of view. 😃 I'm really looking forward to this as it solves a problem that has been bugging me all the time (at least for me). In addition to some of the real world examples already mentioned, here are a few more. jestjs/jest#13495 (comment) prettier/prettier#13158 (comment) There are also some scenarios in babel, but those are less important, currently babel is using So from my personal point of view, although this may make some things available in cjs but not available in esm, it's a lot less negative than the current use of esm in cjs is far behind using cjs in esm. This PR just closes the gap, not even much, as it will take countless years before it becomes stable and widely available in the real world. So unless we want esm to completely replace cjs, the advantages of this PR outweigh the disadvantages. |
I chatted with @mcollina about the HMR requirements and the works on The ability to synchronously import an es module in CJS is still worth to be explored. But it requires more meticulous thought to help people not shot themselves so easily on the wait-for-events-from-another-loop lock problem. |
Possibly related, https://nodejs.org/api/vm.html#class-vmsourcetextmodule has been experimental for quite a while. If I remember correctly it has various issues that needed addressing before it could become stable; and its status is a major blocker for tools like Jest. |
|
Adds SynchronousWorker from the standalone synchronous-worker module directly into Node.js (as Experimental).
The synchronous-worker module was originally written by Anna Henningsen and is incorporated here at Matteo Collina's request and Anna's permission.
This is near verbatim to the original module with necessary adaptations to match Node.js style.
Signed-off-by: James M Snell [email protected]
/cc @addaleax @mcollina