diff --git a/ui/analyse/src/study/studyCtrl.ts b/ui/analyse/src/study/studyCtrl.ts index 6866c4091c0e..9e0ca9a78ebf 100644 --- a/ui/analyse/src/study/studyCtrl.ts +++ b/ui/analyse/src/study/studyCtrl.ts @@ -638,7 +638,7 @@ export default class StudyCtrl { (position.path === this.ctrl.path && position.path === treePath.fromNodeList(this.ctrl.mainline)) ) this.ctrl.jump(newPath); - this.redraw(); + return this.redraw(); }, deleteNode: d => { const position = d.p, @@ -650,7 +650,7 @@ export default class StudyCtrl { if (!this.ctrl.tree.pathExists(d.p.path)) return this.xhrReload(); this.ctrl.tree.deleteNodeAt(position.path); if (this.vm.mode.sticky) this.ctrl.jump(this.ctrl.path); - this.redraw(); + return this.redraw(); }, promote: d => { const position = d.p, @@ -663,7 +663,7 @@ export default class StudyCtrl { if (this.vm.mode.sticky) this.ctrl.jump(this.ctrl.path); else if (this.relay) this.ctrl.jump(d.p.path); this.ctrl.treeVersion++; - this.redraw(); + return this.redraw(); }, reload: this.xhrReload, changeChapter: d => { diff --git a/ui/common/src/throttle.ts b/ui/common/src/throttle.ts index f840889ca6a2..8123afb428af 100644 --- a/ui/common/src/throttle.ts +++ b/ui/common/src/throttle.ts @@ -3,25 +3,61 @@ * flight. Any extra calls are dropped, except the last one, which waits for * the previous call to complete. */ -export function throttlePromise Promise>( +export function throttlePromiseWithResult Promise>( wrapped: T, -): (...args: Parameters) => void { - let current: Promise | undefined; - let afterCurrent: (() => void) | undefined; +): (...args: Parameters) => Promise { + let current: Promise | undefined; + let pending: + | { + run: () => Promise; + reject: () => void; + } + | undefined; - return function (this: any, ...args: Parameters): void { + return function (this: any, ...args: Parameters): Promise { const self = this; - const exec = () => { - afterCurrent = undefined; + const runCurrent = () => { current = wrapped.apply(self, args).finally(() => { current = undefined; - if (afterCurrent) afterCurrent(); + if (pending) { + pending.run(); + pending = undefined; + } }); + return current; }; - if (current) afterCurrent = exec; - else exec(); + if (!current) return runCurrent(); + + pending?.reject(); + const next = new Promise((resolve, reject) => { + pending = { + run: () => + runCurrent().then( + res => { + resolve(res); + return res; + }, + err => { + reject(err); + throw err; + }, + ), + reject: () => reject(new Error('Throttled')), + }; + }); + return next; + }; +} + +/* doesn't fail the promise if it's throttled */ +export function throttlePromise Promise>( + wrapped: T, +): (...args: Parameters) => Promise { + const throttler = throttlePromiseWithResult(wrapped); + return function (this: any, ...args: Parameters): Promise { + return throttler.apply(this, args).catch(() => {}); }; } @@ -49,7 +85,7 @@ export function finallyDelay Promise>( export function throttlePromiseDelay Promise>( delay: (...args: Parameters) => number, wrapped: T, -): (...args: Parameters) => void { +): (...args: Parameters) => Promise { return throttlePromise(finallyDelay(delay, wrapped)); }