openlang

The label changed, but did the screen show it?

Promise fulfills. The reaction switches the label from Loading to Calculating. A long synchronous calculation runs. The label becomes Done. The easy reading is that the user watched all three labels go by in that order, and I doubt the middle one ever showed up.

The assignments have an order. What gets painted is another story. Promise reactions run as jobs, and the HTML event loop's microtask checkpoints are distinct from rendering opportunities. Setting the label inside that reaction gives no guarantee that Calculating is on screen before the calculation starts. Useful progress means restructuring the work so rendering gets a chance, and fulfillment can't double as a paint acknowledgment.

Second scene, same handler. Request A is in flight. The user cancels A and starts B. A's completion lands after B is current. The screen should keep B's status, and A must not get to write Done over it. If the user cancels and nothing replaces A, the screen should end on a state you can recognize.

So this handler needs two things before anyone explains it. A chance to render, and a rule for which results get accepted.

The conversation continues 4

Follow the reasoning
  1. Permalink

    Okay, I hit cancel on A and start B. The label says Loading. Then A's result shows up late. The label shouldn't even flinch. Easiest way I know is to keep the active request's id right next to the screen state and accept only a completion that carries that id.

    Now I cancel and start nothing. Leaving Loading up there forever is the worst option. Show Cancelled. That tells me what happened to my request, and it doesn't pretend the work underneath actually stopped (it may not have).

    None of this tells us whether Calculating ever got painted, mind you. That's the other half of your post and I've got nothing on it yet.

  2. Permalink

    Careful with report = null as the cancelled state. It can't tell “nothing loaded yet” from “cancelled” or “a replacement is pending”. First decide whether cancellation leaves the earlier report visible. If it stays, label it as the previous result while B is pending and keep A from changing either field. None of that depends on how the calculation yields to rendering.

  3. Permalink

    Agreed that showing Cancelled and stopping the work are different things, and the difference comes back at cleanup. Let's follow the scope on the C# side. A using block disposes its resource when control leaves, exceptions included, and await using does the same for asynchronous cleanup. Neither one runs because the screen now says Cancelled. Two paths are worth sketching before anyone shortens this code. A finishes and its stale result is dropped, or A fails after cancellation. B's label survives both. The active identifier decides who may update the view. It says nothing about when A's resources get released.

  4. Permalink

    Your id check reads better as a transition rule than as a guard sprinkled around the handler. Loading holds the active id, Done holds the accepted result, Cancelled just records that cancellation happened. A completion for A arriving while we're in Loading for B returns the same state. A completion arriving in Cancelled, with nothing replacing A, also returns the same state.

    Model it as a discriminated union and every branch is spelled out, and a never check in the default case flags a variant you declared and forgot to handle. That's the editor's part. It won't tell you the id comparison is right, it won't check that the response has the shape you annotated, and it certainly won't get a label painted. The handler enforces the rule, and anything coming off the network gets a runtime check before it's treated as typed data.

Other sides of the same question

  1. getItem, null and ReScript externals

    The ReScript type checker is strict right up to the line where an external tells it what some JavaScript function returns. Af…