Skip to content

Commit 7695712

Browse files
committed
Everything works
1 parent d51c497 commit 7695712

File tree

8 files changed

+103
-87
lines changed

8 files changed

+103
-87
lines changed

packages/plexus-core/src/collection/collection.ts

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -320,13 +320,13 @@ export class CollectionInstance<
320320
// `Batching an collect call for collection ${this.instanceId}`
321321
// )
322322
// // store this in the batchedSetters for execution once batching is over
323-
// this.instance().runtime.batchedCalls.push(() => {
324-
// this.instance().runtime.log(
325-
// 'debug',
326-
// `Batched collect call fulfilled for collection ${this.instanceId}`
327-
// )
328-
// return collectFn(dataToCollect, groups, true)
329-
// })
323+
// // this.instance().runtime.batchedCalls.push(() => {
324+
// // this.instance().runtime.log(
325+
// // 'debug',
326+
// // `Batched collect call fulfilled for collection ${this.instanceId}`
327+
// // )
328+
// // return collectFn(dataToCollect, groups, true)
329+
// // })
330330
// return this
331331
// }
332332

@@ -787,19 +787,15 @@ export class CollectionInstance<
787787
* @returns {this} The new Collection Instance
788788
*/
789789
delete(keys: DataKey | DataKey[]): this {
790-
// the function to remove the data
791-
const rm = (key: DataKey) => {
790+
// if an array, iterate through the keys and remove them each
791+
keys = Array.isArray(keys) ? keys : [keys]
792+
for (let key of keys) {
792793
// key = this.config.keyTransform(key)
793794
key = `${key}`
794795
this._internalStore._data.get(key)?.clean()
795796
this.removeFromGroups(key, this.getGroupsOf(key))
796797
this._internalStore._data.delete(key)
797798
}
798-
// if an array, iterate through the keys and remove them each
799-
keys = Array.isArray(keys) ? keys : [keys]
800-
for (const key of keys) {
801-
rm(key)
802-
}
803799
this.mount()
804800
return this
805801
}
@@ -834,21 +830,18 @@ export class CollectionInstance<
834830
if (!keys.length || !groups.length) return this
835831

836832
this.mount()
837-
const rm = (key) => {
838-
key = `${key}`
839-
for (let groupName of groups) {
840-
if (this.isCreatedGroup(groupName)) {
841-
this._internalStore._groups.get(groupName)?.remove(key)
842-
}
843-
}
844-
}
845833

846834
// if an array, iterate through the keys and remove them from each associated group
847835
// this.instance().runtime.engine.halt()
848836
// const release = this.instance().runtime.engine.halt()
849837
this.instance().runtime.batch(() => {
850838
for (let key of keys) {
851-
rm(key)
839+
key = `${key}`
840+
for (let groupName of groups) {
841+
if (this.isCreatedGroup(groupName)) {
842+
this._internalStore._groups.get(groupName)?.remove(key)
843+
}
844+
}
852845
}
853846
})
854847
return this

packages/plexus-core/src/collection/group.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ interface CollectionGroupStore<DataType = any> {
2828
* A group of data
2929
*/
3030
export class CollectionGroup<
31-
DataType extends Record<string, any> = any,
31+
DataType extends Record<string, any> = any
3232
> extends Watchable<DataType[]> {
3333
private _internalStore: CollectionGroupStore<DataType>
3434
private collection: () => PlexusCollectionInstance<DataType>
@@ -85,6 +85,7 @@ export class CollectionGroup<
8585
this.instance().runtime.broadcast(this.id, this.value)
8686
}
8787
private rebuildDataWatchers(startedFromInnerBatch?: boolean) {
88+
// this.instance().runtime.batch(() => {
8889
this.instance().runtime.log(
8990
'info',
9091
`Group ${this.instanceId} rebuilding data watcher connections...`
@@ -106,6 +107,7 @@ export class CollectionGroup<
106107
if (destroyer) this._internalStore._dataWatcherDestroyers.add(destroyer)
107108

108109
this.runWatchers()
110+
// })
109111
}
110112
/**
111113
* Check if the group contains the given item
@@ -142,7 +144,9 @@ export class CollectionGroup<
142144
this._internalStore._includedKeys.add(key)
143145
}
144146
if (newKeysAdded) {
147+
// this.instance().runtime.batch(() => {
145148
this.rebuildDataWatchers()
149+
// })
146150
}
147151
return this
148152
}

packages/plexus-core/src/instance/engine.ts

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ export interface EngineEventReceiver {
77

88
type EventPayload = { key: String; value: any }
99
export class EventEngine {
10-
private batching: boolean = false
10+
private halted: boolean = false
11+
private halts: number = 0
1112
events: Map<string, Array<EngineEventReceiver>>
1213
pendingEventPayloads: Map<string, EventPayload>
1314

@@ -20,24 +21,30 @@ export class EventEngine {
2021
* Pause and store all events until the return function is called. Once called, all events will be emitted.
2122
*/
2223
halt() {
23-
console.log('halting')
24-
this.batching = true
24+
this.halts++
25+
if (!this.halted) console.log('halting engine...')
26+
27+
this.halted = true
2528
return () => this.release()
2629
}
2730
/**
2831
* Emit all stored concatenated events and resume normal event emitting.
2932
*/
3033
release() {
31-
this.batching = false
34+
this.halts--
35+
if (this.halts > 0) return () => null
36+
this.halted = false
3237
if (this.pendingEventPayloads.size === 0) return
3338
// if (this.batching === false) return
34-
Array.from(this.pendingEventPayloads.entries()).forEach(
35-
([eventId, args]) => {
36-
console.log('releasing', eventId, args)
37-
38-
this.emit(eventId, args)
39-
}
39+
const pendingEntries = Array.from(this.pendingEventPayloads.entries())
40+
console.log(
41+
`releasing ${pendingEntries.length} (${this.pendingEventPayloads.size}) events`
4042
)
43+
for (const [eventId, args] of pendingEntries) {
44+
console.log(`releasing ${eventId} stored payload`)
45+
this.emit(eventId, args)
46+
}
47+
4148
this.pendingEventPayloads.clear()
4249
}
4350

@@ -101,23 +108,22 @@ export class EventEngine {
101108
emit(eventId: string, args: EventPayload) {
102109
// this.schedule.addTask(() => {
103110
// if we're batching, store the event payload
104-
if (this.batching) {
111+
if (this.halted) {
105112
const pendingPayload = this.pendingEventPayloads.get(eventId)
106113

107114
const eventPayload = pendingPayload
108115
? deepMerge<EventPayload>(pendingPayload, args)
109116
: args
110-
console.log(
111-
'Emit occured while batching enabled',
112-
eventId,
113-
pendingPayload,
114-
this.events.get(eventId),
115-
eventPayload
116-
)
117+
// console.log(
118+
// 'Emit occured while batching enabled',
119+
// eventId,
120+
// pendingPayload,
121+
// this.events.get(eventId),
122+
// eventPayload
123+
// )
117124
this.pendingEventPayloads.set(eventId, eventPayload)
118125
return
119126
}
120-
console.log('Emitting', eventId, args)
121127
// run the event listeners for this event id
122128
this.events
123129
.get(eventId)

packages/plexus-core/src/instance/instance.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,6 @@ export function instance(
219219
*/
220220
export function batch<BatchFunction extends () => any | Promise<any> = any>(
221221
fn: BatchFunction
222-
): ReturnType<BatchFunction> {
222+
): ReturnType<BatchFunction> | null {
223223
return instance().runtime.batch(fn)
224224
}

packages/plexus-core/src/instance/runtime.ts

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export class RuntimeInstance {
2626
private initializing = false
2727
private initsCompleted = 0
2828
private batching: boolean = false
29+
// private batchPromise: Promise<any> | null = null
30+
private batchesInProgress: number = 0
2931
batchedCalls: Array<() => any> = []
3032

3133
schedule: Scheduler
@@ -213,52 +215,40 @@ export class RuntimeInstance {
213215
}
214216

215217
/**
216-
* The batch function allows you to run a series of reactive actions in a single transaction
218+
* The batch function allows you to run a series of reactive actions in a single transaction.
219+
* If there is already a batch in progress, the function will be added to the batch and run when the batch is released.
217220
* @param {Function} fn The function to run
218221
*/
219222
batch<BatchFunction extends () => any | Promise<any>>(
220223
fn: BatchFunction
221-
): ReturnType<BatchFunction> {
224+
): ReturnType<BatchFunction> | null {
222225
if (!fn) {
223226
throw new Error('You must provide a function to run in the batch')
224227
}
225-
// if we are already batching, just run the function
228+
// if we are already batching, add the function to the array and return
226229
if (this.batching) {
227-
return fn()
230+
// return fn()
231+
console.log('Already batching something, ')
232+
// ++this.batchesInProgress
233+
this.batchedCalls.push(() => fn())
234+
return null
228235
}
229236

230-
this.startBatching()
231-
this.instance().runtime.log('info', 'Batch function started!')
232-
const releaseBatch = () => {
233-
// if we aren't batching anymore, just return
234-
if (this.batching === false) {
235-
return
236-
}
237-
// stop storing changes and emit the changes
238-
this.batching = false
239-
const unhalt = this.engine.halt()
240-
// call all the pending functions and clear the array
241-
this.batchedCalls.forEach((pendingFn) => pendingFn())
242-
this.batchedCalls.length = 0
243-
244-
// release the reactivity engine
245-
unhalt()
237+
const release = this.startBatching()
246238

247-
this.instance().runtime.log('info', 'Batch function completed!')
248-
}
249239
// run the function. If it returns a promise, wait for it to resolve
250240
const pendingResponse = fn()
251241
if (pendingResponse instanceof Promise) {
252242
return new Promise<ReturnType<BatchFunction>>(async (resolve, reject) => {
253243
// wait for the promise to resolve
254244
const value = await pendingResponse
255245
// release the batch
256-
this.endBatching()
246+
release()
257247
// resolve the promise, return the value of the promise
258248
return resolve(value)
259249
}) as ReturnType<BatchFunction>
260250
}
261-
this.endBatching()
251+
release()
262252
return pendingResponse
263253
}
264254

@@ -274,31 +264,51 @@ export class RuntimeInstance {
274264
* Release the batch
275265
* @returns {void}
276266
*/
277-
endBatching() {
267+
private endBatching() {
278268
// if we aren't batching anymore, just return
279269
if (this.batching === false) {
280270
return
281271
}
282-
// stop storing changes and emit the changes
283-
this.batching = false
272+
// decrement the number of batches in progress
273+
--this.batchesInProgress
274+
// if there are still batches in progress, just return
275+
if (this.batchesInProgress > 0) {
276+
console.log(
277+
`Aborting batch end because ${this.batchesInProgress} batches are still in progress`
278+
)
279+
return
280+
}
281+
282+
const unhalt = this.engine.halt()
283+
console.log(`batchedCalls ${this.batchesInProgress}`, this.batchedCalls)
284284
// call all the pending functions and clear the array
285-
this.batchedCalls.forEach((pendingFn) => pendingFn())
285+
this.batching = false
286+
const v = this.batchesInProgress
287+
for (const pendingFn of this.batchedCalls) {
288+
pendingFn()
289+
console.log(
290+
`Running pending function with ${this.batchesInProgress} others in progress`
291+
)
292+
}
293+
286294
this.batchedCalls.length = 0
287295

288296
// release the reactivity engine
289-
// this.engine.release()
290-
297+
unhalt()
298+
// if(this.batchesInProgress === 0) { unhalt() }
291299
this.instance().runtime.log('info', 'Batch function completed!')
292300
}
293301
/**
294302
* Begin batching any calls to the runtime
295303
* @private
296304
* @returns {Function(): void} A function to release the batch
297305
*/
298-
startBatching() {
306+
private startBatching() {
299307
this.batching = true
308+
++this.batchesInProgress
300309
// hold the reactivity engine and start storing changes
301310
// this.engine.halt()
311+
this.instance().runtime.log('info', 'Batch function started!')
302312
return () => {
303313
this.endBatching()
304314
}

packages/plexus-core/src/scope.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export class Scope {
107107
*/
108108
batch<BatchFunction extends () => any | Promise<any> = any>(
109109
fn: BatchFunction
110-
): ReturnType<BatchFunction> {
110+
): ReturnType<BatchFunction> | null {
111111
return this.instance().runtime.batch(fn)
112112
}
113113
/**

packages/plexus-core/src/watchable.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ export class WatchableMutable<ValueType = any> extends Watchable<ValueType> {
116116
*/
117117
set(newValue?: PlexusWatchableValueInterpreter<ValueType>): this {
118118
if (this.instance().runtime.isBatching) {
119-
this.instance().runtime.batchedCalls.push(() => this.set(newValue))
119+
// this.instance().runtime.batchedCalls.push(() => this.set(newValue))
120+
this.instance().runtime.batch(() => this.set(newValue))
120121
return this
121122
}
122123
this.loading = true

tests/efficiency.test.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,18 @@ afterEach(() => {
5757
})
5858

5959
describe('Efficiency tests for ', () => {
60-
// test('The speed of a plexus collection collecting more than a thousand randomly generated objects into multiple groups', () => {
61-
// // instance({ logLevel: 'debug' })
62-
// console.log('Starting test...')
63-
// console.log('items in collection:', users1k.length)
64-
// usersLite.collect(users1k, ['firstNames'])
65-
// console.log('items in collection:', usersLite.value.length)
66-
// expect(usersLite.value.length).toBe(1000)
67-
// expect(usersLite.groups.firstNames.value.length).toBe(1000)
68-
// // instance({ logLevel: undefined })
69-
// })
60+
test('The speed of a plexus collection collecting more than a thousand randomly generated objects into multiple groups', () => {
61+
instance({ logLevel: 'debug' })
62+
console.log('Starting test...')
63+
console.log(`${users1k.length} items being pulled into collection`)
64+
usersLite.collect(users1k.slice(0, 30), ['firstNames'])
65+
// expect(usersLite.size).toBe(1000)
66+
console.log('items in collection:', usersLite.size)
67+
expect(usersLite.size).toBe(1000)
68+
// expect(usersLite.value.length).toBe(1000)
69+
// expect(usersLite.groups.firstNames.value.length).toBe(1000)
70+
instance({ logLevel: undefined })
71+
})
7072
// test('Testing the same as above but with an absurd amount of data', () => {
7173
// // instance({ logLevel: 'debug' })
7274
// console.log('Starting test...')

0 commit comments

Comments
 (0)