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

Re-write InjectLiveReloadScriptAsync using async/await #6

Merged

Conversation

atifaziz
Copy link
Contributor

@atifaziz atifaziz commented Jun 5, 2019

This PR re-writes WebsocketScriptInjectionHelper.InjectLiveReloadScriptAsync using async/await instead of the more difficult to follow chain of task continuations.

...instead of using task continuations.
@RickStrahl
Copy link
Owner

Thanks Aatif,

Normally I'd agree, but I think in this case there's no need to add the overhead of the extra async await state machine. This code is so simple that I don't think there's anything gained here by adding async/await and adding another method for isolating out the Span.

@atifaziz
Copy link
Contributor Author

atifaziz commented Jun 9, 2019

This code is so simple

Yeah, there's not much happening in there but non-linear code is generally never simpler. I had to read through it very carefully and reason about it and the cognitive tax was high for what should have been a simple function.

I think this could be subjective so let's move on.

…in this case there's no need to add the overhead of the extra async await state machine.

Actually, it's quite the opposite. ContinueWith will always allocate (the closure) whereas the async-await state machinery won't be boxed unless the operation is going to complete asynchronous. With async-await, you'll get the optimisation for free without needing to worry about how the write will occur.

See also the following tweet by @davidfowl:

You can't avoid all allocations even when all the options are defaulted. ContinueWith needs to capture the execution context. That's going to mean at least an object that has both your callback and options.

where he then goes on to add:

In the async await case, the state machine starts off as a struct and once you go async it is then boxed into an object, that boxing allocation is basically reused for everything

that I don't think there's anything gained here by adding async/await

Now let's talk about correctness, which is much harder to get right with ContinueWith, and I guess I should have pointed this out earlier. You see, there's actually a serious bug lurking in there because you neither inspect tb in the callbacks, nor do you use any TaskContinuationOptions (which are hard to get right too and have their own set of issues). This means that the continuations will run even in the face of the WriteAsync tasks failing. It's as if you had a try-catch where the error is completely suppressed. If we ever add a CancellationToken to the mix then the complexity of getting it right with ContinueWith gets compounded. So this isn't just a re-write to make things pretty, ✨ it's to make the code more efficient, correct, sequential to read and debug and consequently simpler to reason about. There's no element of surprise left.

…adding another method for isolating out the Span.

This is just a bonus. 😄 You get a stand-alone, cohesive and reusable extension method and I can't imagine how that can hurt. Also note that the _bodyBytes.ToArray() allocation disappears.

I think the use of Span forced the use of continuations in InjectLiveReloadScriptAsync since Span isn't compatible with hoisting in async-await (although things may change over time) but this PR shows that it's not necessary and fixes bugs as a result.

@RickStrahl RickStrahl merged commit 93cc773 into RickStrahl:master Jun 12, 2019
@RickStrahl
Copy link
Owner

Alright fair enough 😄

This is just a bonus. 😄 You get a stand-alone, cohesive and reusable extension method and I can't imagine how that can hurt. Also note that the _bodyBytes.ToArray() allocation disappears.

This one was your selling point...

Thanks Atif.

@atifaziz atifaziz deleted the InjectLiveReloadScriptAsync-await branch June 12, 2019 08:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants