Ran into this a couple of years ago when developing an indie narrative action platformer which had large-ish Scenes with a whole ton of GameObjects for visual hierarchies AND a lot of logic Components.
LoadSceneAsync is nice in that it loads asynchronously and somewhat smoothly, but then comes the Scene activation as you notice, and that's synchronous - everything on that "game frame" grinds to a halt for however many seconds until
- all hierarchies are activated: depending on the size and complexity of your Scene this takes a shorter or a longer time in itself already
- all Awake and OnEnable logic is run for every Component that implements those methods: almost everything is a Component and any GameObject-hierarchy/Prefab you've built can have however many Components in itself, multiplied by how many and how complicated GameObject hierarchies you have in the Scene and then multiplied again by what those Components do in their Awake/OnEnable. They are likely initializing class members, looking up references to other Components or core systems, and whatever else. Some Components don't need Awake/OnEnable methods while some may do impractically expensive stuff in them. In practice it falls back on developers to figure out by profiling which Components end up spending too long on this stuff and decide what to do about it on a case-by-case basis.
Now, there's less that you can do about Unity's built-in Components that you don't or can't maintain a self-controlled version of: it could be any kind of combination of Components where each Component type could do whatever during its Awake and OnEnable, however slow or expensive.
In general, I think you could split the Scene into groups of smaller hierarchies that are disabled by default and then write some initialization logic in a Scene root-level GameObject that activates those disabled groups one-by-one, making things a little more "asynchronous", sort of: this should smooth out both Unity's and your own Components.
But all of your own Components (and any Package Components where you maintain a self-controlled/overridden/in-Project version of the Package), there's some things you could consider.
- Check your Awake and OnEnable methods, run profiling and find out the top time-sinks during that activation peak. Undoubtedly a lot of Unity's built-in stuff there, but if you have time-sinks of your own you'll spot them on the list soon enough. Figure out some optimizations on a case-by-case basis.
- Figure out some alternative to relying too heavily on Awake/OnEnable methods for your Components. Firstly this is just an example, and I fear far from a perfect solution either. Secondly, I think this is a more involved part-solution, so I'll just describe the concept broadly; in the game I worked on I made something I called DelayedActivator and setup a IPreAwake interface that any heavier-initialization-requiring Components could implement. DelayedActivator hooks up to Unity's Scene-saving and automatically looks up all Components that implement IPreAwake (serializing them as UnityObjects since I couldn't serialize interface instances). When the Scene is activated, DelayedActivator iterates through this serialized list, casting each UnityObject back to IPreAwake and then executing a initialization method declared in the interface for n amount of IPreAwakes per frame (since I wanted smoother loading, to allow displaying some animated loading indicator, etc.), until all IPreAwakes are run and we finally activate a "group"/"folder" root GameObject that holds most or all of the Scene's contents. We still take the hit of running the whole hierarchy activation at once, but this way I could split Awake/OnEnable costs, first into multiple frames of iterating IPreAwake initialization methods, and then later all Unity's builtin Components' Awake/OnEnable (and for those of our own that I didn't turn into IPreAwake implementers).
Summing up: profiling takes some getting used to - I'd probably use the hierarchy/list view (dropdown where you have "Timeline" right now) on that peak-duration frame you already have there and see what Components and methods come up top when sorted by longest time taken (my own? Maybe I can optimize them. Unity's? Maybe I can work around using them, or use less of them, roll my own or build a custom version of them?). If after that there's still a wide-spread issue with a lot of Awake/OnEnable popping up taking time, I'd consider splitting the Scene (either into disabled hierarchies and opening them up one at a time or maybe splitting the entire Scene into multiple sub-Scenes and asynch-loading them in row, only activating gameplay logic after all sub-Scenes are loaded). I'd only resort to wacky hijinx like above-described DelayedActivator/IPreAwake combo if absolutely necessary for a bigger project.