|
|
@@ -1,33 +1,74 @@
|
|
|
const queue: Array<() => void> = []
|
|
|
const postFlushCbs: Array<() => void> = []
|
|
|
+const postFlushCbsForNextTick: Array<() => void> = []
|
|
|
const p = Promise.resolve()
|
|
|
|
|
|
-let hasPendingFlush = false
|
|
|
+let isFlushing = false
|
|
|
+let isFlushingPostCbs = false
|
|
|
|
|
|
export function nextTick(fn?: () => void): Promise<void> {
|
|
|
return p.then(fn)
|
|
|
}
|
|
|
|
|
|
-export function queueJob(job: () => void, postFlushCb?: () => void) {
|
|
|
+export function queueJob(
|
|
|
+ job: () => void,
|
|
|
+ postFlushCb?: () => void,
|
|
|
+ onError?: (err: Error) => void
|
|
|
+) {
|
|
|
if (queue.indexOf(job) === -1) {
|
|
|
queue.push(job)
|
|
|
- if (!hasPendingFlush) {
|
|
|
- hasPendingFlush = true
|
|
|
- nextTick(flushJobs)
|
|
|
+ if (!isFlushing || isFlushingPostCbs) {
|
|
|
+ const p = nextTick(flushJobs)
|
|
|
+ if (onError) p.catch(onError)
|
|
|
}
|
|
|
}
|
|
|
- if (postFlushCb && postFlushCbs.indexOf(postFlushCb) === -1) {
|
|
|
- postFlushCbs.push(postFlushCb)
|
|
|
+ if (postFlushCb) {
|
|
|
+ if (isFlushingPostCbs) {
|
|
|
+ // it's possible for a postFlushCb to queue another job/cb combo,
|
|
|
+ // e.g. triggering a state update inside the updated hook.
|
|
|
+ if (postFlushCbsForNextTick.indexOf(postFlushCb) === -1) {
|
|
|
+ postFlushCbsForNextTick.push(postFlushCb)
|
|
|
+ }
|
|
|
+ } else if (postFlushCbs.indexOf(postFlushCb) === -1) {
|
|
|
+ postFlushCbs.push(postFlushCb)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+const seenJobs = new Map()
|
|
|
+const RECURSION_LIMIT = 100
|
|
|
+
|
|
|
function flushJobs() {
|
|
|
+ seenJobs.clear()
|
|
|
+ isFlushing = true
|
|
|
let job
|
|
|
while ((job = queue.shift())) {
|
|
|
+ if (__DEV__) {
|
|
|
+ if (!seenJobs.has(job)) {
|
|
|
+ seenJobs.set(job, 1)
|
|
|
+ } else {
|
|
|
+ const count = seenJobs.get(job)
|
|
|
+ if (count > RECURSION_LIMIT) {
|
|
|
+ throw new Error('Maximum recursive updates exceeded')
|
|
|
+ } else {
|
|
|
+ seenJobs.set(job, count + 1)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
job()
|
|
|
}
|
|
|
- while ((job = postFlushCbs.shift())) {
|
|
|
- job()
|
|
|
+ isFlushingPostCbs = true
|
|
|
+ if (postFlushCbsForNextTick.length > 0) {
|
|
|
+ const postFlushCbsFromPrevTick = postFlushCbsForNextTick.slice()
|
|
|
+ postFlushCbsForNextTick.length = 0
|
|
|
+ for (let i = 0; i < postFlushCbsFromPrevTick.length; i++) {
|
|
|
+ postFlushCbsFromPrevTick[i]()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (let i = 0; i < postFlushCbs.length; i++) {
|
|
|
+ postFlushCbs[i]()
|
|
|
}
|
|
|
- hasPendingFlush = false
|
|
|
+ postFlushCbs.length = 0
|
|
|
+ isFlushingPostCbs = false
|
|
|
+ isFlushing = false
|
|
|
}
|