@francesconovy wrote:
With Octane, we got some nice and usually helpful errors warning about updating properties that could lead to infinite revalidation bugs, e.g.:
Assertion Failed: You attempted to update `isPending` on `AsyncResource`, 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.
Now generally this is great, but I’ve found places where this is - to me - quite hard to follow and reason about, and I wonder if it might be a bit overzealous in some cases.
For example, I’ve been playing around with building a minimal async resource (until https://github.com/emberjs/rfcs/pull/567 lands) implementation like this:
import { tracked } from '@glimmer/tracking'; import { restartableTask } from 'ember-concurrency-decorators'; export class AsyncResource { promise; @tracked isPending = false; @tracked value; get state() { return { isPending: this.isPending, value: this.value }; } async updatePromise(promise) { if (promise === this.promise) { return; } this._runPromise.perform(promise); } @restartableTask *_runPromise(promise) { this.promise = promise; this.isPending = true; let value = yield promise; this.value = value; this.isPending = false; } }
With an accompanying component:
import Component from '@glimmer/component'; import { AsyncResource } from 'fabscale-app/utils/async-resource'; /* * This component takes a `promise` argument and yields an object with a `value` and a `isPending` property. * Note that the `value` will only change once the passed in promise is resolved! * This means that the last value will be used until a new value is resolved - it will not f.e. switch to `null` in between or similar. */ export default class AwaitPromise extends Component { /* * Arguments: * - promise */ _asyncResource; constructor() { super(...arguments); this._asyncResource = new AsyncResource(); } get promiseData() { this._asyncResource.updatePromise(this.args.promise); return this._asyncResource.state; } }
So usage is like this:
<AwaitPromise @promise={{this.myPromise}} |promiseData|> {{log promiseData}} </AwaitPromise>
Now to me, it looks like this should work, but it triggers the invalidation error. I don’t think (?) this is a bug per se, but basically wanted to ask if that is expected/desired behavior? Or if there are any other ideas on how to better realize such behavior? I ended up with a considerably more complex code where I keep track if the promise has ever resolved and a sprinkle of
yield timeout(1)
before settingthis.isPending
, which kind of works but feels rather over-complicated.
Posts: 1
Participants: 1