Skip to content

Commit

Permalink
Rename internal schedule of watcherCallback
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack-Works committed Jun 30, 2019
1 parent 1550669 commit 596d7c5
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 51 deletions.
59 changes: 30 additions & 29 deletions src/DOM/Watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { LiveSelector } from './LiveSelector'
import differenceWith from 'lodash-es/differenceWith'
import intersectionWith from 'lodash-es/intersectionWith'
import uniqWith from 'lodash-es/uniqWith'
import { Deadline, requestIdleCallback } from '../util/requestIdleCallback'

/**
* Use LiveSelector to watch dom change
Expand All @@ -26,17 +27,17 @@ export abstract class Watcher<T, Before extends Element, After extends Element,
//#region How to start and stop the watcher
/** Let the watcher start to watching */
public startWatch(...args: any[]): this {
this.watching = true
this.isWatching = true
this._warning_forget_watch_.ignored = true
this.watcherCallback()
this.watcherChecker()
return this
}
/** Stop the watcher */
public stopWatch(...args: any[]): void {
this.watching = false
this.isWatching = false
}
/** Is the watcher running */
protected watching = false
protected isWatching = false
//#endregion
//#region useForeach
/** Saved useForeach */
Expand Down Expand Up @@ -103,7 +104,7 @@ export abstract class Watcher<T, Before extends Element, After extends Element,
return this
}
//#endregion
//#region .await() (Once mode)
//#region .then()
/**
* Start the watcher, once it emitted data, stop watching.
* @param map - Map function transform T to Result
Expand All @@ -114,7 +115,10 @@ export abstract class Watcher<T, Before extends Element, After extends Element,
*
* @example
* ```ts
* const value = await watcher.then(x => x.toString())
* const value = await watcher
* const value2 = await watcher(undefined, undefined, { minimalResultsRequired: 5 })
* // If your watcher need parameters for startWatch
* const value3 = await watcher(undefined, undefined, {}, s => s.startWatch(...))
* ```
*/
// The PromiseLike<T> interface
Expand Down Expand Up @@ -432,8 +436,8 @@ export abstract class Watcher<T, Before extends Element, After extends Element,
//#endregion
//#region Watcher callback
/** Should be called every watch */
protected watcherCallback = (deadline?: Deadline) => {
if (!this.watching) return
private watcherChecker = (deadline?: Deadline) => {
if (!this.isWatching) return

const thisNodes: readonly T[] | T | undefined = this.liveSelector.evaluateOnce()

Expand Down Expand Up @@ -559,6 +563,24 @@ export abstract class Watcher<T, Before extends Element, After extends Element,
return this
}
//#endregion
//#region Schedule a watcher callback run
private isWatcherCheckerRunning = false
private needCheckerRunAgainAfterCurrentSchedule = false
protected scheduleWatcherCheck = (deadline?: Deadline | undefined) => {
if (this.isWatcherCheckerRunning) {
this.needCheckerRunAgainAfterCurrentSchedule = true
return
}
this.isWatcherCheckerRunning = true
this.watcherChecker(deadline)
// Now watcherChecker is sync so this path will run at most once.
while (this.needCheckerRunAgainAfterCurrentSchedule) {
this.watcherChecker()
this.needCheckerRunAgainAfterCurrentSchedule = false
}
this.isWatcherCheckerRunning = false
}
//#endregion
//#region Utils
/**
* Get virtual node by key.
Expand Down Expand Up @@ -708,27 +730,6 @@ function applyUseForeachCallback<T>(callback: useForeachReturns<T>) {
}) as applyUseForeach
}
//#endregion
//#region Polyfill for `requestIdleCallback`
/** interface for `requestIdleCallback` */
interface Deadline {
didTimeout: boolean
timeRemaining(): number
}
function requestIdleCallback(fn: (t: Deadline) => void, timeout?: { timeout: number }) {
if ('requestIdleCallback' in window) {
return (window as any).requestIdleCallback(fn)
}
const start = Date.now()
return setTimeout(() => {
fn({
didTimeout: false,
timeRemaining: function() {
return Math.max(0, 50 - (Date.now() - start))
},
})
}, 1)
}
//#endregion
//#region Typescript generic helper
type ResultOf<SingleMode extends boolean, Result> = SingleMode extends true ? (Result) : (Result)[]
function isWatcherWithElement<T>(
Expand Down
12 changes: 1 addition & 11 deletions src/DOM/Watchers/EventWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,11 @@ export class EventWatcher<
super(liveSelector)
this.startWatch()
}
/** Limit computation by rIC */
private rICLock = false
/**
* Use this function as event listener to invoke watcher.
*/
public eventListener = () => {
this.requestIdleCallback(
deadline => {
if (this.rICLock) return
this.rICLock = true
this.watcherCallback(deadline)
this.rICLock = false
},
{ timeout: 500 },
)
this.requestIdleCallback(this.scheduleWatcherCheck, { timeout: 500 })
}
enableSingleMode: () => EventWatcher<T, Before, After, true> = this._enableSingleMode as any
}
2 changes: 1 addition & 1 deletion src/DOM/Watchers/IntervalWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class IntervalWatcher<
/** Start to watch the LiveSelector at a interval(ms). */
startWatch(interval: number) {
super.startWatch()
this.timer = setInterval(() => this.watcherCallback(), interval)
this.timer = setInterval(this.scheduleWatcherCheck, interval)
return this
}
stopWatch() {
Expand Down
13 changes: 3 additions & 10 deletions src/DOM/Watchers/MutationObserverWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,12 @@ export class MutationObserverWatcher<

/** Observe whole document change */
private observer: MutationObserver = new MutationObserver((mutations, observer) => {
this.requestIdleCallback(() => {
if (this.rICLock) return
this.rICLock = true
this.watcherCallback()
this.rICLock = false
})
this.requestIdleCallback(this.scheduleWatcherCheck)
})

/** Limit onMutation computation by rIC */
private rICLock = false
startWatch(options?: MutationObserverInit) {
super.startWatch()
this.watching = true
this.isWatching = true
const option = {
attributes: true,
characterData: true,
Expand All @@ -54,7 +47,7 @@ export class MutationObserverWatcher<
}
const watch = (root?: Node) => {
this.observer.observe(root || document.body, option)
this.watcherCallback()
this.scheduleWatcherCheck()
}
if (document.readyState !== 'complete' && this.consistentWatchRoot === null) {
document.addEventListener('load', () => watch())
Expand Down
26 changes: 26 additions & 0 deletions src/util/requestIdleCallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* @internal
*/
export interface Deadline {
didTimeout: boolean
timeRemaining(): number
}
/**
* @internal
* @param fn function to execute
* @param timeout timeout
*/
export function requestIdleCallback(fn: (t: Deadline) => void, timeout?: { timeout: number }) {
if ('requestIdleCallback' in window) {
return (window as any).requestIdleCallback(fn)
}
const start = Date.now()
return setTimeout(() => {
fn({
didTimeout: false,
timeRemaining: function() {
return Math.max(0, 50 - (Date.now() - start))
},
})
}, 1)
}
3 changes: 3 additions & 0 deletions src/util/sleep.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { requestIdleCallback } from './requestIdleCallback'

/**
* Return a promise that resolved after `time` ms.
* If `time` is `Infinity`, it will never resolve.
Expand All @@ -7,6 +9,7 @@
*/
export const sleep = (time: number) =>
new Promise<void>(resolve => (Number.isFinite(time) ? setTimeout(resolve, time) : void 0))

/**
* Accept a promise and then set a timeout on it. After `time` ms, it will reject.
* @param promise - The promise that you want to set time limit on.
Expand Down

0 comments on commit 596d7c5

Please sign in to comment.