Skip to content
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

feat(nuxt): support parallel plugins #20460

Merged
merged 7 commits into from May 16, 2023
Merged

feat(nuxt): support parallel plugins #20460

merged 7 commits into from May 16, 2023

Conversation

antfu
Copy link
Member

@antfu antfu commented Apr 23, 2023

πŸ”— Linked issue

❓ Type of change

  • πŸ“– Documentation (updates to the documentation, readme or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • πŸ‘Œ Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

πŸ“š Description

It's very convenient that plugins support async functions. However, sometimes it could become a pitfall that users might put too much logic inside them, which leads to slow app initialization. For example, In some cases, a plugin might wait to load some resources like i18n messages. Currently, we execute the plugins one by one sequentially - causing the entire App to stay idle, waiting for the request.

Here is what we had in Elk:

πŸ“ Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@codesandbox
Copy link

codesandbox bot commented Apr 23, 2023

CodeSandbox logoCodeSandbox logoΒ  Open in CodeSandbox Web Editor | VS Code | VS Code Insiders

@antfu antfu requested a review from danielroe April 23, 2023 17:27
Copy link
Member

@pi0 pi0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ‘πŸΌ

Copy link
Member

@danielroe danielroe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this! A couple of thoughts:

  1. Do you think we would still need to respect plugin ordering? In this case, we would need to await plugins (even parallel ones) before proceeding to the next 'chunk' of plugins. Another plugin (deliberately marked later in order) may be relying on every plugin previously having been run first.
  2. ...which brings me to another point. I wonder if we can automatically enable parallelism for plugins that have specified their order, for example. (So all order: 20 plugins would run at the same time unless explicitly disabled.) User plugins (order: 0) would be exempt from this default behaviour. (Counter-point: this may be considered to be a breaking change.)

Later on, I do hope we can make plugin ordering more deterministic at build-time, extracting plugin order and maybe even allowing specified dependsOn with the new plugin name option.

@danielroe danielroe added this to the v3.5 milestone Apr 25, 2023
Copy link
Member

Atinux commented Apr 25, 2023

Do you think we would still need to respect plugin ordering? In this case, we would need to await plugins (even parallel ones) before proceeding to the next 'chunk' of plugins. Another plugin (deliberately marked later in order) may be relying on every plugin previously having been run first.

I guess we can document about using hooks for plugins that depends on others wdyt?

@antfu
Copy link
Member Author

antfu commented Apr 25, 2023

This sounds a bit tricky. I think ideally for this optimization to take good effect, we should make as much parallelism as possible, so I kinda feel make enable parallelism for most of the plugins would be nice (regardless of the breaking change concern).

I am not very should of grouping by order, as the order can be any number and does not have a specific meaning - it would be hard for ppl to say I want to run this plugin before "router" plugin that needs to hardcode an order number before "router" order (a bit magic-number-ish).

I am also not very sure how often would plugins rely on each other, or how often would an async plugin requires to be blocking. Maybe dependsOn would be a better solution but then it might increase the client bundle size a bit

@pi0
Copy link
Member

pi0 commented Apr 25, 2023

I agree that dependency graph for hooks is probably best solution for complex setups and ordering.

Running parallel plugins at end makes sense and is elegant:)

@danielroe danielroe added the 3.x label May 3, 2023
@Aareksio
Copy link
Contributor

Aareksio commented May 5, 2023

Just a quick thought. I do not know how exactly applyPlugin handles errors. Assuming no special error handling, on plugin throw we get uncaught error (in promise) if parallel plugin throws before sequential one finalizes.

async function simulatePlugin(shouldThrow = false, delayMs = 100) {
  await new Promise((resolve) => setTimeout(resolve, delayMs))
  if (shouldThrow) { throw new Error() }
}

async function executePlugins(plugins) {
  const parallels = []
  
  for (const plugin of plugins) {
    const promise = plugin.run()
    if (plugin.parallel) { parallels.push(promise) }
    else { await promise }
  }
  
  await Promise.all(parallels)
}

await executePlugins([
  { run: () => simulatePlugin() },
  { run: () => simulatePlugin(true), parallel: true },
  { run: () => simulatePlugin() },
  { run: () => simulatePlugin() },
])

image

@danielroe danielroe merged commit 433b529 into main May 16, 2023
18 checks passed
@danielroe danielroe deleted the feat/parallel-plugins branch May 16, 2023 08:50
This was referenced May 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants