Assertion when implementing “join running task instance” pattern (task.isRunning + perform) in ember-concurrency 5 / Ember 6

#606 · open · 3 comments

View on GitHub ↗

AmilKey

### Title Assertion when implementing “join running task instance” pattern (`task.isRunning` + `perform`) in ember-concurrency 5 / Ember 6 --- ### Description After upgrading to ember-concurrency v5 (and Ember 6 / autotracking), a common pattern “don’t start task again; if already running, wait for the current instance and reuse its result” now throws an autotracking assertion. Old code (worked in previous versions): ```js if (this.doc.blob.isRunning) { yield waitForProperty(this.doc, 'blob.isIdle'); blob = this.doc.blob.last.value; } else { blob = yield this.doc.blob.perform(true); } ``` Now it fails with: ``` Error: Assertion Failed: You attempted to update `isRunning` on `<Task:blob>`, but it had already been used previously in the same computation. Attempting to update a value after using it in a computation can cause logical errors, infinite revalidation bugs, and performance issues, and is not supported. ``` This seems to be the same class of issue as older reports about updating derived state (`numRunning` / `isRunning`) after reading it in the same computation (#378), but it is still reproducible in EC6 with modern Ember autotracking. ([[GitHub](https://github.com/machty/ember-concurrency/issues/378)][1]) --- ### Expected behavior A safe/idiomatic way to “join” a running task instance should be possible without needing to manually break autotracking computations. Specifically: * If `blob` is running, **do not start a new instance**. * Wait for the current one and return its `value`. --- ### Actual behavior Reading `task.isRunning` and then calling `task.perform()` in the same synchronous tick triggers Ember’s autotracking assertion. This effectively breaks the previous “join existing instance” pattern and pushes users to add artificial async boundaries. --- ### Questions / proposal * Is there a recommended idiomatic pattern for “join existing task instance” in EC6 that doesn’t require manual async boundaries or caching boilerplate? * Could ember-concurrency provide a helper / API for this use case (e.g. `task.join()`, `task.performUnlessRunning()`, or similar), or adjust derived state tagging to make the old pattern safe? --- ### Versions * ember-concurrency: 5.x * ember-source: 6.x

Comments

AmilKey

@machty please have a look at this issue

machty

I don't really have time to dig too deeply on this, but I would just say from my personal experience, a lot of little patterns throughout my codebase had to change to address subtle changes in change-tracking semantics as more things moved to `@tracked` properties and as I upgraded to more recent versions of Ember, and this pattern might just be one more thing that can't exist in exactly the same form it did before. There is high risk that if I (or somebody) tried to fix this one particular case, it would case subtle breakage elsewhere.

AmilKey

> I don't really have time to dig too deeply on this, but I would just say from my personal experience, a lot of little patterns throughout my codebase had to change to address subtle changes in change-tracking semantics as more things moved to `@tracked` properties and as I upgraded to more recent versions of Ember, and this pattern might just be one more thing that can't exist in exactly the same form it did before. There is high risk that if I (or somebody) tried to fix this one particular case, it would case subtle breakage elsewhere. I agree — upgrading to the new Ember + EC versions has been a big challenge, and a lot of previously working patterns broke, forcing us to rewrite quite a bit of code. It would be really helpful to have a migration guide or some guidance to make this transition smoother.