Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 66 additions & 48 deletions src/renderer/queueJob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,34 @@ export class JobQueue {
private isFlushPending = false
private scheduleId = 0
private queue: Job[] = []
private frameInterval = 33
private frameInterval = 16
private initialTime = Date.now()
private pendingJobs = new Map<string, Job>()
private scheduleMode: 'idle' | 'raf' | 'timeout' | null = null

queueJob(job: Job) {
if (job.priority & JOB_PRIORITY.PRIOR) {
job.cb()
} else {
const index = this.findInsertionIndex(job)
if (index >= 0) {
this.queue.splice(index, 0, job)
const existing = this.pendingJobs.get(job.id)
if (existing) {
// 仅更新已有任务的回调与优先级
existing.cb = job.cb
if (job.priority !== existing.priority) {
existing.priority = job.priority
const idx = this.queue.indexOf(existing)
if (idx >= 0) {
this.queue.splice(idx, 1)
const newIndex = this.findInsertionIndex(existing)
this.queue.splice(newIndex, 0, existing)
}
}
} else {
const index = this.findInsertionIndex(job)
if (index >= 0) {
this.queue.splice(index, 0, job)
this.pendingJobs.set(job.id, job)
}
}
}
}
Expand All @@ -33,21 +51,29 @@ export class JobQueue {

clearJobs() {
this.queue.length = 0
this.pendingJobs.clear()
this.isFlushing = false
this.isFlushPending = false
this.cancelScheduleJob()
}

flushJobs() {
flushJobs(deadline?: IdleDeadline) {
this.isFlushPending = false
this.isFlushing = true

const startTime = this.getCurrentTime()
let budget = this.frameInterval
if (deadline && typeof deadline.timeRemaining === 'function') {
const remain = deadline.timeRemaining()
// 防止过长占用单帧
budget = Math.max(0, Math.min(budget, remain))
}

let job
while ((job = this.queue.shift())) {
while (this.queue.length > 0) {
const job = this.queue.shift()!
job.cb()
if (this.getCurrentTime() - startTime >= this.frameInterval) {
this.pendingJobs.delete(job.id)
if (this.getCurrentTime() - startTime >= budget) {
break
}
}
Expand All @@ -63,14 +89,14 @@ export class JobQueue {
this.isFlushPending = false
this.isFlushing = true

let job
while ((job = this.queue.shift())) {
while (this.queue.length > 0) {
const job = this.queue.shift()!
try {
job.cb()
} catch (error) {
// eslint-disable-next-line
console.log(error)
console.error(error)
}
this.pendingJobs.delete(job.id)
}

this.isFlushing = false
Expand All @@ -94,33 +120,42 @@ export class JobQueue {
}

private scheduleJob() {
if (this.scheduleId) {
this.cancelScheduleJob()
}
if ('requestIdleCallback' in window) {
if (this.scheduleId) {
this.cancelScheduleJob()
}
this.scheduleId = window.requestIdleCallback(this.flushJobs.bind(this), {
timeout: 100,
})
this.scheduleMode = 'idle'
this.scheduleId = window.requestIdleCallback(
(deadline: IdleDeadline) => this.flushJobs(deadline),
{
timeout: 100,
},
)
} else if ('requestAnimationFrame' in window) {
this.scheduleMode = 'raf'
this.scheduleId = (window as Window).requestAnimationFrame(() =>
this.flushJobs(),
)
} else {
if (this.scheduleId) {
this.cancelScheduleJob()
}
this.scheduleId = (window as Window).setTimeout(this.flushJobs.bind(this))
this.scheduleMode = 'timeout'
this.scheduleId = (window as Window).setTimeout(() => this.flushJobs())
}
}

private cancelScheduleJob() {
if ('cancelIdleCallback' in window) {
if (this.scheduleId) {
window.cancelIdleCallback(this.scheduleId)
}
this.scheduleId = 0
if (!this.scheduleId) return
if (this.scheduleMode === 'idle' && 'cancelIdleCallback' in window) {
window.cancelIdleCallback(this.scheduleId as number)
} else if (
this.scheduleMode === 'raf' &&
'cancelAnimationFrame' in window
) {
window.cancelAnimationFrame(this.scheduleId as number)
} else {
if (this.scheduleId) {
clearTimeout(this.scheduleId)
}
this.scheduleId = 0
clearTimeout(this.scheduleId as number)
}
this.scheduleId = 0
this.scheduleMode = null
}

private getCurrentTime() {
Expand All @@ -145,20 +180,3 @@ export enum JOB_PRIORITY {
RenderNode = /**/ 1 << 3,
PRIOR = /* */ 1 << 20,
}

// function findInsertionIndex(job: Job) {
// let start = 0
// for (let i = 0, len = queue.length; i < len; i += 1) {
// const j = queue[i]
// if (j.id === job.id) {
// console.log('xx', j.bit, job.bit)
// }
// if (j.id === job.id && (job.bit ^ (job.bit & j.bit)) === 0) {
// return -1
// }
// if (j.priority <= job.priority) {
// start += 1
// }
// }
// return start
// }
Loading