Skip to content

Commit

Permalink
Fix sync batching in keepFresh
Browse files Browse the repository at this point in the history
  • Loading branch information
igorkamyshev committed Apr 20, 2023
1 parent f13c40e commit 33b32c8
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/calm-carrots-compete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@farfetched/core': patch
---

Fix sync batching in `keepFresh`
2 changes: 1 addition & 1 deletion packages/core/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"size": {
"executor": "./tools/executors/size-limit:size-limit",
"options": {
"limit": "18.5 kB",
"limit": "18.9 kB",
"outputPath": "dist/packages/core"
},
"dependsOn": [
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/libs/patronus/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ export {
export { type FetchingStatus } from './status';
export { time } from './time';
export { and } from './and';
export { syncBatch } from './sync_batch';
84 changes: 84 additions & 0 deletions packages/core/src/libs/patronus/sync_batch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copied and adopted https://github.com/effector/patronum/blob/main/src/debounce/index.ts

import {
createEvent,
createStore,
type Event,
type EventAsReturnType,
sample,
attach,
} from 'effector';

export function syncBatch<T>(clock: Event<T>): EventAsReturnType<T> {
const saveTimeoutId = createEvent<NodeJS.Timeout>();

const $timeoutId = createStore<NodeJS.Timeout | null>(null, {
serialize: 'ignore',
}).on(saveTimeoutId, (_, id) => id);

const saveReject = createEvent<() => void>();

const $rejecter = createStore<(() => void) | null>(null, {
serialize: 'ignore',
}).on(saveReject, (_, rj) => rj);

const tick = createEvent<T>();

const timerFx = attach({
source: {
timeoutId: $timeoutId,
rejectPromise: $rejecter,
},
effect: ({ timeoutId, rejectPromise }) => {
if (timeoutId) clearTimeout(timeoutId);
if (rejectPromise) rejectPromise();
return new Promise((resolve, reject) => {
saveReject(reject);
saveTimeoutId(setTimeout(resolve, 0));
});
},
});
$rejecter.reset(timerFx.done);
$timeoutId.reset(timerFx.done);

// It's ok - nothing will ever start unless source is triggered
const $payload = createStore<T[]>([], { serialize: 'ignore' }).on(
clock,
(_, payload) => [payload]
);

const $canTick = createStore(true, { serialize: 'ignore' });

const triggerTick = createEvent();

$canTick
.on(triggerTick, () => false)
.on(
[
tick,
// debounce timeout can be restarted in later ticks
timerFx,
],
() => true
);

sample({
clock: clock,
filter: $canTick,
target: triggerTick,
});

sample({
clock: triggerTick,
target: timerFx,
});

sample({
source: $payload,
clock: timerFx.done,
fn: ([payload]) => payload,
target: tick,
});

return tick;
}
6 changes: 2 additions & 4 deletions packages/core/src/trigger_api/keep_fresh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ import {
sample,
is,
createStore,
merge,
} from 'effector';

import { type Query } from '../query/type';
import { divide, get, isEqual } from '../libs/lohyphen';
import {
not,
and,
delay,
syncBatch,
normalizeSourced,
extractSource,
} from '../libs/patronus';
Expand Down Expand Up @@ -139,8 +138,7 @@ export function keepFresh<Params>(

// @ts-expect-error TS cannot get that if query.$idle is false, then $latestParams is Params
sample({
// Use sync batching
clock: delay({ clock: forceFresh, timeout: 0 }),
clock: syncBatch(forceFresh),
source: query.__.$latestParams,
filter: not(query.$idle),
target: query.refresh,
Expand Down

0 comments on commit 33b32c8

Please sign in to comment.