Skip to content

Commit d945d91

Browse files
Merge pull request #665 from NullVoxPopuli/retryable-trackedFunction
feat(trackedFunction): add a retry method
2 parents 9a31146 + 8244049 commit d945d91

File tree

3 files changed

+70
-22
lines changed

3 files changed

+70
-22
lines changed

ember-resources/src/util/function.ts

+39-20
Original file line numberDiff line numberDiff line change
@@ -126,25 +126,9 @@ export function trackedFunction<Return>(...passedArgs: UseFunctionArgs<Return>)
126126
}
127127

128128
return resource<State<Return>>(context, (hooks) => {
129-
let state = new State(initialValue);
129+
let state = new State(fn, hooks, initialValue);
130130

131-
(async () => {
132-
try {
133-
let notQuiteValue = fn(hooks);
134-
let promise = Promise.resolve(notQuiteValue);
135-
136-
waitForPromise(promise);
137-
138-
let result = await promise;
139-
140-
state.error = undefined;
141-
state.resolvedValue = result;
142-
} catch (e) {
143-
state.error = e;
144-
} finally {
145-
state.isResolved = true;
146-
}
147-
})();
131+
state.retry();
148132

149133
return state;
150134
});
@@ -158,10 +142,18 @@ export class State<Value> {
158142
@tracked resolvedValue?: Value;
159143
@tracked error?: unknown;
160144

161-
constructor(public initialValue?: Value) {}
145+
#fn: ResourceFn<Value>;
146+
#hooks: Hooks;
147+
#initialValue: Value | undefined;
148+
149+
constructor(fn: ResourceFn<Value>, hooks: Hooks, initialValue?: Value) {
150+
this.#fn = fn;
151+
this.#hooks = hooks;
152+
this.#initialValue = initialValue;
153+
}
162154

163155
get value() {
164-
return this.resolvedValue || this.initialValue || null;
156+
return this.resolvedValue || this.#initialValue || null;
165157
}
166158

167159
get isPending() {
@@ -175,6 +167,33 @@ export class State<Value> {
175167
get isError() {
176168
return Boolean(this.error);
177169
}
170+
171+
/**
172+
* Will re-invoke the function passed to `trackedFunction`
173+
* this will also re-set some properties on the `State` instance.
174+
* This is the same `State` instance as before, as the `State` instance
175+
* is tied to the `fn` passed to `trackedFunction`
176+
*
177+
* `error` or `resolvedValue` will remain as they were previously
178+
* until this promise resolves, and then they'll be updated to the new values.
179+
*/
180+
retry = async () => {
181+
try {
182+
let notQuiteValue = this.#fn(this.#hooks);
183+
let promise = Promise.resolve(notQuiteValue);
184+
185+
waitForPromise(promise);
186+
187+
let result = await promise;
188+
189+
this.error = undefined;
190+
this.resolvedValue = result;
191+
} catch (e) {
192+
this.error = e;
193+
} finally {
194+
this.isResolved = true;
195+
}
196+
};
178197
}
179198

180199
/**

testing/ember-app/tests/utils/function-resource/rendering-test.ts

-2
Original file line numberDiff line numberDiff line change
@@ -435,8 +435,6 @@ module('Utils | resource | rendering', function (hooks) {
435435
});
436436
});
437437

438-
module('persistent state', function () {});
439-
440438
module('with a wrapper', function () {
441439
test('lifecycle', async function (assert) {
442440
const Wrapper = (initial: number) => {

testing/ember-app/tests/utils/function/rendering-test.ts

+31
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,37 @@ module('Utils | trackedFunction | rendering', function (hooks) {
4040
assert.dom('out').hasText('2');
4141
});
4242

43+
test('it is retryable', async function (assert) {
44+
let count = 0;
45+
46+
class Test extends Component {
47+
data = trackedFunction(this, () => {
48+
assert.step(`ran trackedFunction ${count++}`);
49+
50+
return count;
51+
});
52+
}
53+
54+
const TestComponent = setComponentTemplate(
55+
hbs`
56+
<out>{{this.data.value}}</out>
57+
<button type='button' {{on 'click' this.data.retry}}></button>`,
58+
Test
59+
);
60+
61+
this.setProperties({ TestComponent });
62+
63+
await render(hbs`<this.TestComponent />`);
64+
65+
assert.dom('out').hasText('1');
66+
67+
await click('button');
68+
69+
assert.dom('out').hasText('2');
70+
71+
assert.verifySteps(['ran trackedFunction 0', 'ran trackedFunction 1']);
72+
});
73+
4374
test('async functions update when the promise resolves', async function (assert) {
4475
class Test extends Component {
4576
@tracked multiplier = 1;

0 commit comments

Comments
 (0)