scheduler.ts 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. import { handleError, ErrorTypes } from './errorHandling'
  2. const queue: Function[] = []
  3. const postFlushCbs: Function[] = []
  4. const p = Promise.resolve()
  5. let isFlushing = false
  6. export function nextTick(fn?: () => void): Promise<void> {
  7. return fn ? p.then(fn) : p
  8. }
  9. export function queueJob(job: () => void) {
  10. if (queue.indexOf(job) === -1) {
  11. queue.push(job)
  12. if (!isFlushing) {
  13. nextTick(flushJobs)
  14. }
  15. }
  16. }
  17. export function queuePostFlushCb(cb: Function | Function[]) {
  18. if (Array.isArray(cb)) {
  19. postFlushCbs.push.apply(postFlushCbs, cb)
  20. } else {
  21. postFlushCbs.push(cb)
  22. }
  23. if (!isFlushing) {
  24. nextTick(flushJobs)
  25. }
  26. }
  27. const dedupe = (cbs: Function[]): Function[] => Array.from(new Set(cbs))
  28. export function flushPostFlushCbs() {
  29. if (postFlushCbs.length) {
  30. const cbs = dedupe(postFlushCbs)
  31. postFlushCbs.length = 0
  32. for (let i = 0; i < cbs.length; i++) {
  33. cbs[i]()
  34. }
  35. }
  36. }
  37. const RECURSION_LIMIT = 100
  38. type JobCountMap = Map<Function, number>
  39. function flushJobs(seenJobs?: JobCountMap) {
  40. isFlushing = true
  41. let job
  42. if (__DEV__) {
  43. seenJobs = seenJobs || new Map()
  44. }
  45. while ((job = queue.shift())) {
  46. if (__DEV__) {
  47. const seen = seenJobs as JobCountMap
  48. if (!seen.has(job)) {
  49. seen.set(job, 1)
  50. } else {
  51. const count = seen.get(job) as number
  52. if (count > RECURSION_LIMIT) {
  53. throw new Error(
  54. 'Maximum recursive updates exceeded. ' +
  55. "You may have code that is mutating state in your component's " +
  56. 'render function or updated hook.'
  57. )
  58. } else {
  59. seen.set(job, count + 1)
  60. }
  61. }
  62. }
  63. try {
  64. job()
  65. } catch (err) {
  66. handleError(err, null, ErrorTypes.SCHEDULER)
  67. }
  68. }
  69. flushPostFlushCbs()
  70. isFlushing = false
  71. // some postFlushCb queued jobs!
  72. // keep flushing until it drains.
  73. if (queue.length) {
  74. flushJobs(seenJobs)
  75. }
  76. }