1
1
import { ErrorCodes , callWithErrorHandling } from './errorHandling'
2
2
import { isArray } from '@vue/shared'
3
3
4
- const queue : ( Function | null ) [ ] = [ ]
4
+ export interface Job {
5
+ ( ) : void
6
+ id ?: number
7
+ }
8
+
9
+ const queue : ( Job | null ) [ ] = [ ]
5
10
const postFlushCbs : Function [ ] = [ ]
6
11
const p = Promise . resolve ( )
7
12
8
13
let isFlushing = false
9
14
let isFlushPending = false
10
15
11
16
const RECURSION_LIMIT = 100
12
- type CountMap = Map < Function , number >
17
+ type CountMap = Map < Job | Function , number >
13
18
14
19
export function nextTick ( fn ?: ( ) => void ) : Promise < void > {
15
20
return fn ? p . then ( fn ) : p
16
21
}
17
22
18
- export function queueJob ( job : ( ) => void ) {
23
+ export function queueJob ( job : Job ) {
19
24
if ( ! queue . includes ( job ) ) {
20
25
queue . push ( job )
21
26
queueFlush ( )
22
27
}
23
28
}
24
29
25
- export function invalidateJob ( job : ( ) => void ) {
30
+ export function invalidateJob ( job : Job ) {
26
31
const i = queue . indexOf ( job )
27
32
if ( i > - 1 ) {
28
33
queue [ i ] = null
@@ -45,11 +50,9 @@ function queueFlush() {
45
50
}
46
51
}
47
52
48
- const dedupe = ( cbs : Function [ ] ) : Function [ ] => [ ...new Set ( cbs ) ]
49
-
50
53
export function flushPostFlushCbs ( seen ?: CountMap ) {
51
54
if ( postFlushCbs . length ) {
52
- const cbs = dedupe ( postFlushCbs )
55
+ const cbs = [ ... new Set ( postFlushCbs ) ]
53
56
postFlushCbs . length = 0
54
57
if ( __DEV__ ) {
55
58
seen = seen || new Map ( )
@@ -63,13 +66,27 @@ export function flushPostFlushCbs(seen?: CountMap) {
63
66
}
64
67
}
65
68
69
+ const getId = ( job : Job ) => ( job . id == null ? Infinity : job . id )
70
+
66
71
function flushJobs ( seen ?: CountMap ) {
67
72
isFlushPending = false
68
73
isFlushing = true
69
74
let job
70
75
if ( __DEV__ ) {
71
76
seen = seen || new Map ( )
72
77
}
78
+
79
+ // Sort queue before flush.
80
+ // This ensures that:
81
+ // 1. Components are updated from parent to child. (because parent is always
82
+ // created before the child so its render effect will have smaller
83
+ // priority number)
84
+ // 2. If a component is unmounted during a parent component's update,
85
+ // its update can be skipped.
86
+ // Jobs can never be null before flush starts, since they are only invalidated
87
+ // during execution of another flushed job.
88
+ queue . sort ( ( a , b ) => getId ( a ! ) - getId ( b ! ) )
89
+
73
90
while ( ( job = queue . shift ( ) ) !== undefined ) {
74
91
if ( job === null ) {
75
92
continue
@@ -88,7 +105,7 @@ function flushJobs(seen?: CountMap) {
88
105
}
89
106
}
90
107
91
- function checkRecursiveUpdates ( seen : CountMap , fn : Function ) {
108
+ function checkRecursiveUpdates ( seen : CountMap , fn : Job | Function ) {
92
109
if ( ! seen . has ( fn ) ) {
93
110
seen . set ( fn , 1 )
94
111
} else {
0 commit comments