refactor: promise and tickers

This commit is contained in:
cha0s 2024-07-22 01:31:52 -05:00
parent 6997e108c0
commit 6eac298671
13 changed files with 157 additions and 160 deletions

View File

@ -8,7 +8,7 @@ export default class Interactive extends Component {
const script = this.interactScriptInstance.clone(); const script = this.interactScriptInstance.clone();
script.context.initiator = initiator; script.context.initiator = initiator;
const {Ticking} = ecs.get(this.entity); const {Ticking} = ecs.get(this.entity);
Ticking.addTickingPromise(script.tickingPromise()); Ticking.add(script.ticker());
} }
get interacting() { get interacting() {
return !!this.$$interacting; return !!this.$$interacting;

View File

@ -11,7 +11,7 @@ export default class Plant extends Component {
} }
grow() { grow() {
const {Ticking} = ecs.get(this.entity); const {Ticking} = ecs.get(this.entity);
Ticking.addTickingPromise(this.growScriptInstance.tickingPromise()); Ticking.add(this.growScriptInstance.ticker());
} }
}; };
} }

View File

@ -5,30 +5,30 @@ export default class Ticking extends Component {
return class TickingInstance extends super.instanceFromSchema() { return class TickingInstance extends super.instanceFromSchema() {
$$finished = []; $$finished = [];
$$tickingPromises = []; $$tickers = [];
addTickingPromise(tickingPromise) { add(ticker) {
this.$$tickingPromises.push(tickingPromise); this.$$tickers.push(ticker);
tickingPromise.then(() => { ticker.then(() => {
this.$$finished.push(tickingPromise); this.$$finished.push(ticker);
}); });
} }
reset() { reset() {
this.$$finished = []; this.$$finished = [];
this.$$tickingPromises = []; this.$$tickers = [];
} }
tick(elapsed) { tick(elapsed) {
for (const tickingPromise of this.$$finished) { for (const ticker of this.$$finished) {
this.$$tickingPromises.splice( this.$$tickers.splice(
this.$$tickingPromises.indexOf(tickingPromise), this.$$tickers.indexOf(ticker),
1, 1,
); );
} }
this.$$finished = []; this.$$finished = [];
for (const tickingPromise of this.$$tickingPromises) { for (const ticker of this.$$tickers) {
tickingPromise.tick(elapsed); ticker.tick(elapsed);
} }
} }

View File

@ -20,7 +20,7 @@ export default class Wielder extends Component {
script.context.ecs = ecs; script.context.ecs = ecs;
script.context.item = activeItem; script.context.item = activeItem;
script.context.wielder = entity; script.context.wielder = entity;
Ticking.addTickingPromise(script.tickingPromise()); Ticking.add(script.ticker());
} }
} }
} }

View File

@ -69,14 +69,14 @@ export default class Colliders extends System {
const script = entity.Collider.collisionStartScriptInstance.clone(); const script = entity.Collider.collisionStartScriptInstance.clone();
script.context.intersections = intersections; script.context.intersections = intersections;
script.context.other = other; script.context.other = other;
entity.Ticking.addTickingPromise(script.tickingPromise()); entity.Ticking.add(script.ticker());
} }
if (other.Collider.collisionStartScriptInstance) { if (other.Collider.collisionStartScriptInstance) {
const script = other.Collider.collisionStartScriptInstance.clone(); const script = other.Collider.collisionStartScriptInstance.clone();
script.context.intersections = intersections script.context.intersections = intersections
.map(([l, r]) => [r, l]); .map(([l, r]) => [r, l]);
script.context.other = entity; script.context.other = entity;
other.Ticking.addTickingPromise(script.tickingPromise()); other.Ticking.add(script.ticker());
} }
} }
for (const i in intersections) { for (const i in intersections) {
@ -127,12 +127,12 @@ export default class Colliders extends System {
if (entity.Collider.collisionEndScriptInstance) { if (entity.Collider.collisionEndScriptInstance) {
const script = entity.Collider.collisionEndScriptInstance.clone(); const script = entity.Collider.collisionEndScriptInstance.clone();
script.context.other = other; script.context.other = other;
entity.Ticking.addTickingPromise(script.tickingPromise()); entity.Ticking.add(script.ticker());
} }
if (other.Collider.collisionEndScriptInstance) { if (other.Collider.collisionEndScriptInstance) {
const script = other.Collider.collisionEndScriptInstance.clone(); const script = other.Collider.collisionEndScriptInstance.clone();
script.context.other = entity; script.context.other = entity;
other.Ticking.addTickingPromise(script.tickingPromise()); other.Ticking.add(script.ticker());
} }
} }
} }

View File

@ -1,4 +1,4 @@
import TickingPromise from '@/util/ticking-promise.js'; import {Ticker} from '@/util/promise.js';
export default function delta(object, properties) { export default function delta(object, properties) {
const deltas = {}; const deltas = {};
@ -12,7 +12,7 @@ export default function delta(object, properties) {
deltas[key] = delta; deltas[key] = delta;
} }
let stop; let stop;
const promise = new TickingPromise( const promise = new Ticker(
(resolve) => { (resolve) => {
stop = resolve; stop = resolve;
}, },

View File

@ -1,5 +1,4 @@
import {withResolvers} from '@/util/promise.js'; import {Ticker, withResolvers} from '@/util/promise.js';
import TickingPromise from '@/util/ticking-promise.js';
const Modulators = { const Modulators = {
flat: () => 0.5, flat: () => 0.5,
@ -44,7 +43,7 @@ export default function lfo(object, properties) {
oscillators[key] = oscillator; oscillators[key] = oscillator;
} }
let stop; let stop;
const promise = new TickingPromise( const promise = new Ticker(
(resolve) => { (resolve) => {
stop = resolve; stop = resolve;
Promise.all(promises).then(resolve); Promise.all(promises).then(resolve);

View File

@ -1,3 +1,10 @@
export const all = Promise.all.bind(Promise);
export const allSettled = Promise.allSettled.bind(Promise);
export const any = Promise.any.bind(Promise);
export const race = Promise.race.bind(Promise);
export const reject = Promise.reject.bind(Promise);
export const resolve = Promise.resolve.bind(Promise);
export function withResolvers() { export function withResolvers() {
let resolve, reject, promise = new Promise((res, rej) => { let resolve, reject, promise = new Promise((res, rej) => {
resolve = res; resolve = res;
@ -5,3 +12,57 @@ export function withResolvers() {
}); });
return {promise, reject, resolve}; return {promise, reject, resolve};
} }
export class Ticker extends Promise {
constructor(executor, ticker) {
let _reject;
let _resolve;
super((resolve, reject) => {
_reject = reject;
_resolve = resolve;
if (executor) {
executor(resolve, reject);
}
});
this.reject = _reject;
this.resolve = _resolve;
this.ticker = ticker;
}
static all(promises) {
const tickers = [];
for (let i = 0; i < promises.length; i++) {
const promise = promises[i];
if (promise instanceof Ticker) {
tickers.push(promise);
// After resolution, stop ticking the promise.
promise.then(() => {
tickers.splice(tickers.indexOf(promise), 1);
});
}
}
/* v8 ignore next 3 */
if (0 === tickers.length) {
return super.all(promises);
}
return new Ticker(
(resolve, reject) => {
super.all(promises)
.then(resolve)
/* v8 ignore next */
.catch(reject);
},
(elapsed) => {
for (let i = 0; i < tickers.length; i++) {
tickers[i].tick(elapsed);
}
},
);
}
tick(elapsed) {
this.ticker(elapsed, this.resolve, this.reject);
}
}

View File

@ -1,17 +1,17 @@
import {expect, test} from 'vitest'; import {expect, test} from 'vitest';
import TickingPromise from './ticking-promise.js'; import {Ticker} from './promise.js';
test('runs executor', async () => { test('runs executor', async () => {
expect( expect(
await new TickingPromise((resolve) => { await new Ticker((resolve) => {
resolve(32); resolve(32);
}), }),
) )
.to.equal(32); .to.equal(32);
expect( expect(
async () => { async () => {
await new TickingPromise((resolve, reject) => { await new Ticker((resolve, reject) => {
reject(new Error('')); reject(new Error(''));
}) })
} }
@ -22,7 +22,7 @@ test('runs executor', async () => {
test('ticks and resolves', async () => { test('ticks and resolves', async () => {
let done = false; let done = false;
let e = 0; let e = 0;
const tp = new TickingPromise(undefined, (elapsed, resolve) => { const tp = new Ticker(undefined, (elapsed, resolve) => {
e += elapsed; e += elapsed;
if (1 === e) { if (1 === e) {
done = true; done = true;
@ -49,7 +49,7 @@ test('ticks and resolves', async () => {
test('ticks and rejects', async () => { test('ticks and rejects', async () => {
let caught = false; let caught = false;
const tp = new TickingPromise(undefined, (elapsed, resolve, reject) => { const tp = new Ticker(undefined, (elapsed, resolve, reject) => {
reject(new Error()); reject(new Error());
}); });
tp.catch(() => { tp.catch(() => {
@ -66,21 +66,21 @@ test('ticks and rejects', async () => {
test('handles all', async () => { test('handles all', async () => {
let done = 0; let done = 0;
let e1 = 0, e2 = 0; let e1 = 0, e2 = 0;
const tp1 = new TickingPromise(undefined, (elapsed, resolve) => { const tp1 = new Ticker(undefined, (elapsed, resolve) => {
e1 += elapsed; e1 += elapsed;
if (1 === e1) { if (1 === e1) {
done += 1; done += 1;
resolve(16); resolve(16);
} }
}); });
const tp2 = new TickingPromise(undefined, (elapsed, resolve) => { const tp2 = new Ticker(undefined, (elapsed, resolve) => {
e2 += elapsed; e2 += elapsed;
if (2 === e2) { if (2 === e2) {
done += 1; done += 1;
resolve(32); resolve(32);
} }
}); });
const tpa = TickingPromise.all([ const tpa = Ticker.all([
Promise.resolve(8), Promise.resolve(8),
tp1, tp1,
tp2, tp2,

View File

@ -2,10 +2,10 @@ import {parse as acornParse} from 'acorn';
import {LRUCache} from 'lru-cache'; import {LRUCache} from 'lru-cache';
import Sandbox from '@/astride/sandbox.js'; import Sandbox from '@/astride/sandbox.js';
import TickingPromise from '@/util/ticking-promise.js';
import delta from '@/util/delta.js'; import delta from '@/util/delta.js';
import lfo from '@/util/lfo.js'; import lfo from '@/util/lfo.js';
import * as MathUtil from '@/util/math.js'; import * as MathUtil from '@/util/math.js';
import * as PromiseUtil from '@/util/promise.js';
import transition from '@/util/transition.js'; import transition from '@/util/transition.js';
function parse(code, options = {}) { function parse(code, options = {}) {
@ -43,7 +43,7 @@ export default class Script {
delta, delta,
lfo, lfo,
Math: MathUtil, Math: MathUtil,
Promise, Promise: PromiseUtil,
transition, transition,
wait: (seconds) => ( wait: (seconds) => (
new Promise((resolve) => { new Promise((resolve) => {
@ -95,7 +95,7 @@ export default class Script {
tick(elapsed, resolve, reject) { tick(elapsed, resolve, reject) {
if (this.promise) { if (this.promise) {
if (this.promise instanceof TickingPromise) { if (this.promise instanceof PromiseUtil.Ticker) {
this.promise.tick(elapsed); this.promise.tick(elapsed);
} }
return; return;
@ -128,8 +128,8 @@ export default class Script {
} }
} }
tickingPromise() { ticker() {
return new TickingPromise( return new PromiseUtil.Ticker(
() => {}, () => {},
(elapsed, resolve, reject) => { (elapsed, resolve, reject) => {
this.tick(elapsed, resolve, reject); this.tick(elapsed, resolve, reject);
@ -137,18 +137,18 @@ export default class Script {
); );
} }
static tickingPromise(code, context = {}) { static ticker(code, context = {}) {
let tickingPromise; let ticker;
return new TickingPromise( return new PromiseUtil.Ticker(
(resolve) => { (resolve) => {
this.fromCode(code, context) this.fromCode(code, context)
.then((script) => { .then((script) => {
tickingPromise = script.tickingPromise(); ticker = script.ticker();
resolve(tickingPromise); resolve(ticker);
}) })
}, },
(elapsed) => { (elapsed) => {
tickingPromise?.tick?.(elapsed); ticker?.tick?.(elapsed);
}, },
); );
} }

View File

@ -1,53 +0,0 @@
export default class TickingPromise extends Promise {
constructor(executor, ticker) {
let _reject;
let _resolve;
super((resolve, reject) => {
_reject = reject;
_resolve = resolve;
if (executor) {
executor(resolve, reject);
}
});
this.reject = _reject;
this.resolve = _resolve;
this.ticker = ticker;
}
static all(promises) {
const tickingPromises = [];
for (let i = 0; i < promises.length; i++) {
const promise = promises[i];
if (promise instanceof TickingPromise) {
tickingPromises.push(promise);
// After resolution, stop ticking the promise.
promise.then(() => {
tickingPromises.splice(tickingPromises.indexOf(promise), 1);
});
}
}
/* v8 ignore next 3 */
if (0 === tickingPromises.length) {
return super.all(promises);
}
return new TickingPromise(
(resolve, reject) => {
super.all(promises)
.then(resolve)
/* v8 ignore next */
.catch(reject);
},
(elapsed) => {
for (let i = 0; i < tickingPromises.length; i++) {
tickingPromises[i].tick(elapsed);
}
},
);
}
tick(elapsed) {
this.ticker(elapsed, this.resolve, this.reject);
}
}

View File

@ -1,4 +1,4 @@
import TickingPromise from '@/util/ticking-promise.js'; import {Ticker} from '@/util/promise.js';
/* eslint-disable */ /* eslint-disable */
const Easing = { const Easing = {
@ -152,7 +152,7 @@ export default function transition(object, properties) {
transitions[key] = transition; transitions[key] = transition;
} }
let stop; let stop;
const promise = new TickingPromise( const promise = new Ticker(
(resolve) => { (resolve) => {
stop = resolve; stop = resolve;
}, },

View File

@ -48,71 +48,61 @@ for (let i = 0; i < 10; ++i) {
}, },
) )
const l = lfo( tomato.Ticking.add(
d.deltas.y, Promise.Ticker.all([
{ d.promise,
delta: { lfo(
count: 1, d.deltas.y,
frequency: 0.5, {
magnitude: 64, delta: {
median: 0, count: 1,
offset: -0.5, frequency: 0.5,
}, magnitude: 64,
}, median: 0,
) offset: -0.5,
const ls = lfo( },
tomato.Sprite,
{
scaleX: {
count: 1,
frequency: 0.5,
magnitude: 0.333,
median: 0.333,
elapsed: 0.25,
offset: -0.5,
},
scaleY: {
count: 1,
frequency: 0.5,
magnitude: 0.333,
median: 0.333,
elapsed: 0.25,
offset: -0.5,
},
},
)
tomato.Ticking.addTickingPromise(
d.promise,
);
tomato.Ticking.addTickingPromise(
l.promise,
);
tomato.Ticking.addTickingPromise(
ls.promise,
);
tomato.Ticking.addTickingPromise(
delta(
tomato.Position,
{
x: {
duration: 0.5,
delta: (12 * x) + Math.random() * 8,
}, },
}, ).promise,
).promise, lfo(
); tomato.Sprite,
tomato.Ticking.addTickingPromise( {
delta( scaleX: {
tomato.Position, count: 1,
{ frequency: 0.5,
y: { magnitude: 0.333,
duration: 0.5, median: 0.333,
delta: (12 * y) + Math.random() * 8, elapsed: 0.25,
offset: -0.5,
},
scaleY: {
count: 1,
frequency: 0.5,
magnitude: 0.333,
median: 0.333,
elapsed: 0.25,
offset: -0.5,
},
}, },
}, ).promise,
).promise, delta(
tomato.Position,
{
x: {
duration: 0.5,
delta: (12 * x) + Math.random() * 8,
},
},
).promise,
delta(
tomato.Position,
{
y: {
duration: 0.5,
delta: (12 * y) + Math.random() * 8,
},
},
).promise,
]),
); );
} }