Skip to content

Commit 4e3dc50

Browse files
committed
perf: Optimize yieldToEventLoop calls to reduce unnecessary wait times
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
1 parent 37f6681 commit 4e3dc50

5 files changed

Lines changed: 70 additions & 18 deletions

File tree

src/lib/Diff.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,10 +413,13 @@ export default class Diff<
413413
}
414414

415415
async toJSONAsync() {
416+
let iterations = 0
416417
return Parallel.map(
417418
this.getActions(),
418419
async(action: A) => {
419-
await yieldToEventLoop()
420+
if (++iterations % 1000 === 0) {
421+
await yieldToEventLoop()
422+
}
420423
return {
421424
...action,
422425
payload: await action.payload.clone(false).toJSONAsync(),
@@ -469,8 +472,11 @@ export default class Diff<
469472
A2 extends Action<L1, L2>
470473
>(json) {
471474
const diff: Diff<L1, L2, A2> = new Diff()
475+
let iterations = 0
472476
await Parallel.map(json, async(action: A2): Promise<void> => {
473-
await yieldToEventLoop()
477+
if (++iterations % 1000 === 0) {
478+
await yieldToEventLoop()
479+
}
474480
action.payload = hydrate<L1>(action.payload)
475481
action.oldItem = action.oldItem && hydrate<L2>(action.oldItem)
476482
diff.commit(action)

src/lib/Scanner.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export default class Scanner<L1 extends TItemLocation, L2 extends TItemLocation>
2020
private hashSettings: IHashSettings
2121
private checkHashes: boolean
2222
private hasCache: boolean
23+
private iterations = 0
2324

2425
private result: ScanResult<L2, L1>
2526

@@ -62,7 +63,9 @@ export default class Scanner<L1 extends TItemLocation, L2 extends TItemLocation>
6263

6364
async diffFolder(oldFolder:Folder<L1>, newFolder:Folder<L2>):Promise<void> {
6465
// give the browser time to breathe
65-
await yieldToEventLoop()
66+
if (++this.iterations % 1000 === 0) {
67+
await yieldToEventLoop()
68+
}
6669
if (this.checkHashes) {
6770
const hasChanged = await this.folderHasChanged(oldFolder, newFolder)
6871
if (!hasChanged) {
@@ -314,9 +317,12 @@ export default class Scanner<L1 extends TItemLocation, L2 extends TItemLocation>
314317
sources[action.oldItem.parentId] = true
315318
})
316319

320+
let iterations = 0
317321
for (const folderId in sources) {
318322
// Give the browser time to breathe
319-
await yieldToEventLoop()
323+
if (++iterations % 1000 === 0) {
324+
await yieldToEventLoop()
325+
}
320326
const oldFolder = this.oldTree.findItem(ItemType.FOLDER, folderId) as Folder<L1>
321327
if (!oldFolder) {
322328
// In case a MOVE's old parent was removed
@@ -330,7 +336,9 @@ export default class Scanner<L1 extends TItemLocation, L2 extends TItemLocation>
330336

331337
for (const folderId in targets) {
332338
// Give the browser time to breathe
333-
await yieldToEventLoop()
339+
if (++iterations % 1000 === 0) {
340+
await yieldToEventLoop()
341+
}
334342
const newFolder = this.newTree.findItem(ItemType.FOLDER, folderId) as Folder<L2>
335343
const duplicate = this.result.REORDER.getActions().find(a => String(a.payload.id) === String(newFolder.id))
336344
if (duplicate) {

src/lib/Tree.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ interface IItemIndex<L extends TItemLocation> {
2929
[ItemType.FOLDER]: Record<string|number,Folder<L>>,
3030
}
3131

32+
let HASH_ITERATIONS = 0
33+
3234
export class Bookmark<L extends TItemLocation> {
3335
public type = ItemType.BOOKMARK
3436
public id: string | number
@@ -184,8 +186,11 @@ export class Bookmark<L extends TItemLocation> {
184186
const result = {}
185187
// eslint-disable-next-line @typescript-eslint/no-this-alias
186188
let obj = this
189+
let iterations = 1
187190
while (obj instanceof Bookmark) {
188-
await yieldToEventLoop()
191+
if (++iterations % 1000 === 0) {
192+
await yieldToEventLoop()
193+
}
189194
Object.entries(obj).forEach(([key, value]) => {
190195
if (key === 'index') return
191196
if (!(key in result)) {
@@ -369,13 +374,16 @@ export class Folder<L extends TItemLocation> {
369374
async traverse(
370375
fn: (item: TItem<L>, folder: Folder<L>) => void
371376
): Promise<void> {
377+
let iterations = 0
372378
await Parallel.each(
373379
this.children,
374380
async(item) => {
375381
await fn(item, this)
376382
if (item.type === 'folder') {
377383
// give the browser time to breathe
378-
await yieldToEventLoop()
384+
if (++iterations % 1000 === 0) {
385+
await yieldToEventLoop()
386+
}
379387
await item.traverse(fn)
380388
}
381389
},
@@ -422,7 +430,9 @@ export class Folder<L extends TItemLocation> {
422430
throw new Error("Trying to calculate hash of a folder that isn't loaded")
423431
}
424432

425-
await yieldToEventLoop()
433+
if (++HASH_ITERATIONS % 1000 === 0) {
434+
await yieldToEventLoop()
435+
}
426436

427437
const children = this.children.slice()
428438
if (!preserveOrder) {
@@ -535,8 +545,11 @@ export class Folder<L extends TItemLocation> {
535545
const result: Folder<L> = {} as any as Folder<L>
536546
// eslint-disable-next-line @typescript-eslint/no-this-alias
537547
let obj = this
548+
let iterations = 1
538549
while (obj instanceof Folder) {
539-
await yieldToEventLoop()
550+
if (++iterations % 1000 === 0) {
551+
await yieldToEventLoop()
552+
}
540553
await Parallel.map(Object.entries(obj), async([key, value]) => {
541554
if (key === 'index') return
542555
if (!(key in result)) {

src/lib/strategies/Default.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ export default class SyncProcess {
8787
protected cancelPromise: Promise<void>
8888
protected cancelCb: (error: any) => void
8989

90+
// We're counting the number of calls to addMappings so we can conditionally yieldToEventLoop every 1000th call
91+
protected mappingIterations = 0
92+
protected executeIterations = 0
93+
9094
constructor(
9195
mappings:Mappings,
9296
localTree:TLocalTree,
@@ -1038,7 +1042,9 @@ export default class SyncProcess {
10381042
donePlan: PlanStage3<TOppositeLocation<L1>, TItemLocation, L1>
10391043
): Promise<void> {
10401044
// defer execution of actions to allow the this.canceled check below to work when cancelling in interrupt tests
1041-
await yieldToEventLoop()
1045+
if (++this.executeIterations % 1000 === 0) {
1046+
await yieldToEventLoop()
1047+
}
10421048
Logger.log('Executing action ', action)
10431049

10441050
if (this.canceled) {
@@ -1245,7 +1251,9 @@ export default class SyncProcess {
12451251
donePlan: PlanStage3<TOppositeLocation<L1>, TItemLocation, L1>
12461252
): Promise<void> {
12471253
// defer execution of actions to allow the this.canceled check below to work when cancelling in interrupt tests
1248-
await yieldToEventLoop()
1254+
if (++this.executeIterations % 1000 === 0) {
1255+
await yieldToEventLoop()
1256+
}
12491257
Logger.log('Executing action ', action)
12501258

12511259
if (this.canceled) {
@@ -1268,7 +1276,9 @@ export default class SyncProcess {
12681276
diff: Diff<L1, TItemLocation, UpdateAction<L1, TItemLocation> | MoveAction<L1, TItemLocation>>,
12691277
donePlan: PlanStage3<TItemLocation, TItemLocation, L1>): Promise<void> {
12701278
// defer execution of actions to allow the this.canceled check below to work when cancelling in interrupt tests
1271-
await yieldToEventLoop()
1279+
if (++this.executeIterations % 1000 === 0) {
1280+
await yieldToEventLoop()
1281+
}
12721282
Logger.log('Executing action ', action)
12731283

12741284
if (this.canceled) {
@@ -1392,8 +1402,11 @@ export default class SyncProcess {
13921402

13931403
const isUsingTabs = await this.localTree.isUsingBrowserTabs?.()
13941404

1405+
let iterations = 0
13951406
await Parallel.each(reorderings.getActions(), async(action) => {
1396-
await yieldToEventLoop()
1407+
if (++iterations % 1000 === 0) {
1408+
await yieldToEventLoop()
1409+
}
13971410
Logger.log('Executing reorder action', `${action.type} Payload: #${action.payload.id}[${action.payload.title}]${'url' in action.payload ? `(${action.payload.url})` : ''} parentId: ${action.payload.parentId}`)
13981411
const item = action.payload
13991412

@@ -1433,7 +1446,9 @@ export default class SyncProcess {
14331446
}
14341447

14351448
async addMapping(resource:TResource<TItemLocation>, item:TItem<TItemLocation>, newId:string|number):Promise<void> {
1436-
await yieldToEventLoop()
1449+
if (++this.mappingIterations % 1000 === 0) {
1450+
await yieldToEventLoop()
1451+
}
14371452
let localId, remoteId
14381453
if (resource === this.server) {
14391454
localId = item.id
@@ -1568,6 +1583,7 @@ export default class SyncProcess {
15681583
}
15691584
}
15701585
const membersToPersist = this.getMembersToPersist()
1586+
let iterations = 0
15711587
return {
15721588
strategy: 'default',
15731589
...this.staticContinuation,
@@ -1602,7 +1618,9 @@ export default class SyncProcess {
16021618
return [key, await diff.toJSONAsync()]
16031619
}
16041620
if (diff && diff.toJSON) {
1605-
await yieldToEventLoop()
1621+
if (++iterations % 1000 === 0) {
1622+
await yieldToEventLoop()
1623+
}
16061624
return [key, diff.toJSON()]
16071625
}
16081626
return [key, diff]
@@ -1615,7 +1633,9 @@ export default class SyncProcess {
16151633
return [key, await value.toJSONAsync()]
16161634
}
16171635
if (value && value.toJSON) {
1618-
await yieldToEventLoop()
1636+
if (++iterations % 1000 === 0) {
1637+
await yieldToEventLoop()
1638+
}
16191639
return [key, value.toJSON()]
16201640
}
16211641
return [key, value]

src/lib/strategies/Unidirectional.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ export default class UnidirectionalSyncProcess extends DefaultStrategy {
521521
}
522522
}
523523
const membersToPersist = this.getMembersToPersist()
524+
let iterations = 0
524525
return {
525526
strategy: 'unidirectional',
526527
...this.staticContinuation,
@@ -555,7 +556,9 @@ export default class UnidirectionalSyncProcess extends DefaultStrategy {
555556
return [key, await diff.toJSONAsync()]
556557
}
557558
if (diff && diff.toJSON) {
558-
await yieldToEventLoop()
559+
if (++iterations % 1000 === 0) {
560+
await yieldToEventLoop()
561+
}
559562
return [key, diff.toJSON()]
560563
}
561564
return [key, diff]
@@ -568,7 +571,9 @@ export default class UnidirectionalSyncProcess extends DefaultStrategy {
568571
return [key, await value.toJSONAsync()]
569572
}
570573
if (value && value.toJSON) {
571-
await yieldToEventLoop()
574+
if (++iterations % 1000 === 0) {
575+
await yieldToEventLoop()
576+
}
572577
return [key, value.toJSON()]
573578
}
574579
return [key, value]

0 commit comments

Comments
 (0)