Skip to content

Commit 2660a61

Browse files
authored
fix: await async check function in retryWithBackoff (sugarlabs#7539)
* fix: await async check function in retryWithBackoff check() may be async per JSDoc. Without await, it returns a Promise (always truthy), causing onSuccess to fire immediately on the first iteration with the Promise object instead of the resolved value. Retries never occur for async checks that resolve to false. Fixes sugarlabs#7185 * fix: only await check() when it returns a Promise to preserve sync behavior
1 parent 802651e commit 2660a61

2 files changed

Lines changed: 43 additions & 1 deletion

File tree

js/__tests__/retryWithBackoff.test.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,5 +347,46 @@ describe("retryWithBackoff", () => {
347347
expect(callCount).toBe(2);
348348
expect(onSuccess).toHaveBeenCalled();
349349
});
350+
351+
it("should await an async check function and retry until it resolves truthy", async () => {
352+
let callCount = 0;
353+
const onSuccess = jest.fn();
354+
355+
await retryWithBackoff({
356+
check: async () => {
357+
callCount++;
358+
return callCount >= 3 ? "ready" : false;
359+
},
360+
onSuccess,
361+
delayFn: instantDelay,
362+
maxRetries: 5
363+
});
364+
365+
expect(callCount).toBe(3);
366+
expect(onSuccess).toHaveBeenCalledWith("ready");
367+
});
368+
369+
it("should not defer sync checks to microtask queue", async () => {
370+
const order = [];
371+
372+
const promise = retryWithBackoff({
373+
check: () => {
374+
order.push("check");
375+
return true;
376+
},
377+
onSuccess: () => {
378+
order.push("onSuccess");
379+
},
380+
delayFn: instantDelay
381+
});
382+
383+
// Sync check + onSuccess should run before any awaited microtask
384+
order.push("after-call");
385+
await promise;
386+
387+
expect(order[0]).toBe("check");
388+
expect(order[1]).toBe("onSuccess");
389+
expect(order[2]).toBe("after-call");
390+
});
350391
});
351392
});

js/utils/retryWithBackoff.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ const retryWithBackoff = async ({
9494
}));
9595

9696
for (let count = 0; count <= maxRetries; count++) {
97-
const result = check();
97+
const raw = check();
98+
const result = raw instanceof Promise ? await raw : raw;
9899

99100
if (result) {
100101
await onSuccess(result);

0 commit comments

Comments
 (0)