Skip to content

Commit

Permalink
use a single global queue to run continuations on the RAF
Browse files Browse the repository at this point in the history
  • Loading branch information
bordoley committed Feb 15, 2024
1 parent daf38fe commit ff22bf6
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 107 deletions.
63 changes: 34 additions & 29 deletions mod/integrations/web/AnimationFrameScheduler.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions packages/example-web/src/example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ import {
__stream,
__using,
} from "@reactive-js/core/concurrent/Observable/effects";
import { __animate, __animationFrameScheduler } from "@reactive-js/core/integrations/web/effects";
import {
__animate,
__animationFrameScheduler,
} from "@reactive-js/core/integrations/web/effects";
import { Wordle } from "./wordle.js";
import Measure from "./measure.js";
import * as WindowLocation from "@reactive-js/core/integrations/web/WindowLocation";
Expand Down Expand Up @@ -310,7 +313,7 @@ const RxComponent = createComponent(
return Observable.computeDeferred(() => {
const { windowLocation } = __await(props);
const uri = __await(windowLocation);

const animationScheduler = __animationFrameScheduler();

const createScheduler = __constant(() => {
Expand Down
6 changes: 1 addition & 5 deletions packages/example-web/src/mouse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ import React, { useMemo } from "react";
import ReactDOMClient from "react-dom/client";
import * as Observable from "@reactive-js/core/concurrent/Observable";
import { useAnimate } from "@reactive-js/core/integrations/react/web";
import {
pipe,
pipeSomeLazy,
returns,
} from "@reactive-js/core/functions";
import { pipe, pipeSomeLazy, returns } from "@reactive-js/core/functions";
import {
__await,
__bindMethod,
Expand Down
152 changes: 81 additions & 71 deletions src/integrations/web/AnimationFrameScheduler.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as CurrentTime from "../../__internal__/CurrentTime.js";
import {
Map_delete,
Map_get,
Expand Down Expand Up @@ -57,17 +58,8 @@ const create = /*@__PURE__*/ (() => {
const AnimationFrameScheduler_delayScheduler = Symbol(
"AnimationFrameScheduler_delayScheduler",
);
const AnimationFrameScheduler_rafCallback = Symbol(
"AnimationFrameScheduler_rafCallback",
);

const AnimationFrameScheduler_rafQueue = Symbol(
"AnimationFrameScheduler_rafQueue",
);

const AnimationFrameScheduler_rafIsRunning = Symbol(
"AnimationFrameScheduler_rafIsRunning",
);
const RafScheduler_schedule = Symbol("RafScheduler_schedule");

const raf = globalObject.requestAnimationFrame;

Expand All @@ -78,80 +70,105 @@ const create = /*@__PURE__*/ (() => {

type TProperties = {
[AnimationFrameScheduler_delayScheduler]: SchedulerLike;
[AnimationFrameScheduler_rafCallback]: () => void;
[AnimationFrameScheduler_rafQueue]: IndexedQueueLike<ContinuationLike>;
[AnimationFrameScheduler_rafIsRunning]: boolean;
};

return mixInstanceFactory(
include(CurrentTimeSchedulerMixin),
function AnimationFrameScheduler(
instance: Omit<ContinuationSchedulerLike, typeof SchedulerLike_now> &
TProperties,
hostScheduler: SchedulerLike,
): SchedulerLike & DisposableLike {
init(CurrentTimeSchedulerMixin, instance, 5);
interface RafScheduler {
[RafScheduler_schedule](continuation: ContinuationLike): void;
}

instance[AnimationFrameScheduler_delayScheduler] = hostScheduler;
instance[AnimationFrameScheduler_rafQueue] = IndexedQueue.create();
instance[AnimationFrameScheduler_rafIsRunning] = false;
const rafScheduler: RafScheduler = (() => {
const RafScheduler_rafQueue = Symbol("RafScheduler_rafQueue");

const RafScheduler_rafIsRunning = Symbol("RafScheduler_rafIsRunning");

interface RafSchedulerImpl {
[RafScheduler_rafQueue]: IndexedQueueLike<ContinuationLike>;
[RafScheduler_rafIsRunning]: boolean;
[RafScheduler_schedule](continuation: ContinuationLike): void;
}

instance[AnimationFrameScheduler_rafCallback] = () => {
const startTime = instance[SchedulerLike_now];
const workQueue = instance[AnimationFrameScheduler_rafQueue];
const rafCallback = () => {
const startTime = CurrentTime.now();
const workQueue = rafScheduler[RafScheduler_rafQueue];

rafScheduler[RafScheduler_rafQueue] = IndexedQueue.create();

let continuation: Optional<ContinuationLike> = none;
while (
((continuation = workQueue[QueueLike_dequeue]()), isSome(continuation))
) {
continuation[ContinuationLike_run]();

const elapsedTime = CurrentTime.now() - startTime;
if (elapsedTime > 5 /*ms*/) {
break;
}
}

instance[AnimationFrameScheduler_rafQueue] = IndexedQueue.create();
const continuationsCount = workQueue[QueueLike_count];
const newWorkQueue = rafScheduler[RafScheduler_rafQueue];
const newContinuationsCount = newWorkQueue[QueueLike_count];

if (continuationsCount > 0 && newContinuationsCount === 0) {
rafScheduler[RafScheduler_rafQueue] = workQueue;
} else if (continuationsCount > 0) {
// Merge the job queues copying the newly enqueued jobs
// onto the original queue.
let continuation: Optional<ContinuationLike> = none;
while (
((continuation = workQueue[QueueLike_dequeue]()),
((continuation = newWorkQueue[QueueLike_dequeue]()),
isSome(continuation))
) {
continuation[ContinuationLike_run]();

const elapsedTime = instance[SchedulerLike_now] - startTime;
if (elapsedTime > 5 /*ms*/) {
break;
}
workQueue[QueueableLike_enqueue](continuation);
}
rafScheduler[RafScheduler_rafQueue] = workQueue;
}

const continuationsQueueCount =
rafScheduler[RafScheduler_rafQueue][QueueLike_count];
if (continuationsQueueCount > 0) {
raf(rafCallback);
} else {
rafScheduler[RafScheduler_rafIsRunning] = false;
}
};

const rafScheduler: RafSchedulerImpl = {
[RafScheduler_rafQueue]: IndexedQueue.create<ContinuationLike>(),
[RafScheduler_rafIsRunning]: false,
[RafScheduler_schedule](
this: RafSchedulerImpl,
continuation: ContinuationLike,
) {
this[RafScheduler_rafQueue][QueueableLike_enqueue](continuation);

const continuationsCount = workQueue[QueueLike_count];
const newWorkQueue = instance[AnimationFrameScheduler_rafQueue];
const newContinuationsCount = newWorkQueue[QueueLike_count];

if (continuationsCount > 0 && newContinuationsCount === 0) {
instance[AnimationFrameScheduler_rafQueue] = workQueue;
} else if (continuationsCount > 0) {
// Merge the job queues copying the newly enqueued jobs
// onto the original queue.
let continuation: Optional<ContinuationLike> = none;
while (
((continuation = newWorkQueue[QueueLike_dequeue]()),
isSome(continuation))
) {
workQueue[QueueableLike_enqueue](continuation);
}
instance[AnimationFrameScheduler_rafQueue] = workQueue;
if (!this[RafScheduler_rafIsRunning]) {
this[RafScheduler_rafIsRunning] = true;
raf(rafCallback);
}
},
};

const continuationsQueueCount =
instance[AnimationFrameScheduler_rafQueue][QueueLike_count];
if (continuationsQueueCount > 0) {
raf(instance[AnimationFrameScheduler_rafCallback]);
} else {
instance[AnimationFrameScheduler_rafIsRunning] = false;
}
};
return rafScheduler;
})();

return mixInstanceFactory(
include(CurrentTimeSchedulerMixin),
function AnimationFrameScheduler(
instance: Omit<ContinuationSchedulerLike, typeof SchedulerLike_now> &
TProperties,
hostScheduler: SchedulerLike,
): SchedulerLike & DisposableLike {
init(CurrentTimeSchedulerMixin, instance, 5);

instance[AnimationFrameScheduler_delayScheduler] = hostScheduler;

hostScheduler[DisposableContainerLike_add](instance);

return instance;
},
props<TProperties>({
[AnimationFrameScheduler_delayScheduler]: none,
[AnimationFrameScheduler_rafCallback]: none,
[AnimationFrameScheduler_rafQueue]: none,
[AnimationFrameScheduler_rafIsRunning]: false,
}),
{
[ContinuationSchedulerLike_shouldYield]: true,
Expand Down Expand Up @@ -181,14 +198,7 @@ const create = /*@__PURE__*/ (() => {
Disposable.addTo(continuation),
);
} else {
this[AnimationFrameScheduler_rafQueue][QueueableLike_enqueue](
continuation,
);

if (!this[AnimationFrameScheduler_rafIsRunning]) {
this[AnimationFrameScheduler_rafIsRunning] = true;
raf(this[AnimationFrameScheduler_rafCallback]);
}
rafScheduler[RafScheduler_schedule](continuation);
}
},
},
Expand Down

0 comments on commit ff22bf6

Please sign in to comment.