6
6
* found in the LICENSE file at https://angular.io/license
7
7
*/
8
8
9
+ import {
10
+ getActiveConsumer ,
11
+ Consumer as InteropConsumer ,
12
+ Signal as InteropSignal ,
13
+ setActiveConsumer ,
14
+ } from './interop_lib' ;
15
+
9
16
// Required as the signals library is in a separate package, so we need to explicitly ensure the
10
17
// global `ngDevMode` type is defined.
11
18
declare const ngDevMode : boolean | undefined ;
12
19
13
- /**
14
- * The currently active consumer `ReactiveNode`, if running code in a reactive context.
15
- *
16
- * Change this via `setActiveConsumer`.
17
- */
18
- let activeConsumer : ReactiveNode | null = null ;
19
20
let inNotificationPhase = false ;
20
21
21
- type Version = number & { __brand : 'Version' } ;
22
+ export type Version = number & { __brand : 'Version' } ;
22
23
23
24
/**
24
25
* Global epoch counter. Incremented whenever a source signal is set.
@@ -32,16 +33,6 @@ let epoch: Version = 1 as Version;
32
33
*/
33
34
export const SIGNAL = /* @__PURE__ */ Symbol ( 'SIGNAL' ) ;
34
35
35
- export function setActiveConsumer ( consumer : ReactiveNode | null ) : ReactiveNode | null {
36
- const prev = activeConsumer ;
37
- activeConsumer = consumer ;
38
- return prev ;
39
- }
40
-
41
- export function getActiveConsumer ( ) : ReactiveNode | null {
42
- return activeConsumer ;
43
- }
44
-
45
36
export function isInNotificationPhase ( ) : boolean {
46
37
return inNotificationPhase ;
47
38
}
@@ -53,7 +44,6 @@ export interface Reactive {
53
44
export function isReactive ( value : unknown ) : value is Reactive {
54
45
return ( value as Partial < Reactive > ) [ SIGNAL ] !== undefined ;
55
46
}
56
-
57
47
export const REACTIVE_NODE : ReactiveNode = {
58
48
version : 0 as Version ,
59
49
lastCleanEpoch : 0 as Version ,
@@ -62,6 +52,7 @@ export const REACTIVE_NODE: ReactiveNode = {
62
52
producerLastReadVersion : undefined ,
63
53
producerIndexOfThis : undefined ,
64
54
nextProducerIndex : 0 ,
55
+ hasInteropSignalDep : false ,
65
56
liveConsumerNode : undefined ,
66
57
liveConsumerIndexOfThis : undefined ,
67
58
consumerAllowSignalWrites : false ,
@@ -70,6 +61,8 @@ export const REACTIVE_NODE: ReactiveNode = {
70
61
producerRecomputeValue : ( ) => { } ,
71
62
consumerMarkedDirty : ( ) => { } ,
72
63
consumerOnSignalRead : ( ) => { } ,
64
+ producerOnAccess : ( ) => { } ,
65
+ producerValue : ( ) => { } ,
73
66
} ;
74
67
75
68
/**
@@ -143,6 +136,11 @@ export interface ReactiveNode {
143
136
*/
144
137
nextProducerIndex : number ;
145
138
139
+ /**
140
+ * Whether this consumer has any interop signals as dependencies.
141
+ */
142
+ hasInteropSignalDep : boolean ;
143
+
146
144
/**
147
145
* Array of consumers of this producer that are "live" (they require push notifications).
148
146
*
@@ -180,6 +178,9 @@ export interface ReactiveNode {
180
178
*/
181
179
consumerOnSignalRead ( node : unknown ) : void ;
182
180
181
+ producerOnAccess ( node : unknown ) : void ;
182
+ producerValue ( node : unknown ) : unknown ;
183
+
183
184
/**
184
185
* Called when the signal becomes "live"
185
186
*/
@@ -211,27 +212,32 @@ interface ProducerNode extends ReactiveNode {
211
212
/**
212
213
* Called by implementations when a producer's signal is read.
213
214
*/
214
- export function producerAccessed ( node : ReactiveNode ) : void {
215
+ export function producerAccessed < T > ( node : ReactiveNode & InteropSignal < T > ) : void {
215
216
if ( inNotificationPhase ) {
216
217
throw new Error (
217
218
typeof ngDevMode !== 'undefined' && ngDevMode
218
219
? `Assertion error: signal read during notification phase`
219
220
: '' ,
220
221
) ;
221
222
}
223
+ getActiveConsumer ( ) ?. addProducer ( node ) ;
224
+ }
222
225
223
- if ( activeConsumer === null ) {
224
- // Accessed outside of a reactive context, so nothing to record.
225
- return ;
226
- }
227
-
226
+ export function internalProducerAccessed (
227
+ node : ReactiveNode ,
228
+ activeConsumer : ReactiveNode & InteropConsumer ,
229
+ ) : void {
228
230
activeConsumer . consumerOnSignalRead ( node ) ;
229
231
230
232
// This producer is the `idx`th dependency of `activeConsumer`.
231
233
const idx = activeConsumer . nextProducerIndex ++ ;
232
234
233
235
assertConsumerNode ( activeConsumer ) ;
234
236
237
+ if ( node . hasInteropSignalDep ) {
238
+ activeConsumer . hasInteropSignalDep = true ;
239
+ }
240
+
235
241
if ( idx < activeConsumer . producerNode . length && activeConsumer . producerNode [ idx ] !== node ) {
236
242
// There's been a change in producers since the last execution of `activeConsumer`.
237
243
// `activeConsumer.producerNode[idx]` holds a stale dependency which will be be removed and
@@ -275,7 +281,7 @@ export function producerIncrementEpoch(): void {
275
281
* Ensure this producer's `version` is up-to-date.
276
282
*/
277
283
export function producerUpdateValueVersion ( node : ReactiveNode ) : void {
278
- if ( ! node . dirty && node . lastCleanEpoch === epoch ) {
284
+ if ( ! node . dirty && node . lastCleanEpoch === epoch && ! node . hasInteropSignalDep ) {
279
285
// Even non-live consumers can skip polling if they previously found themselves to be clean at
280
286
// the current epoch, since their dependencies could not possibly have changed (such a change
281
287
// would've increased the epoch).
@@ -324,7 +330,10 @@ export function producerNotifyConsumers(node: ReactiveNode): void {
324
330
* based on the current consumer context.
325
331
*/
326
332
export function producerUpdatesAllowed ( ) : boolean {
327
- return activeConsumer ?. consumerAllowSignalWrites !== false ;
333
+ return (
334
+ ( getActiveConsumer ( ) as ( InteropConsumer & ReactiveNode ) | null ) ?. consumerAllowSignalWrites !==
335
+ false
336
+ ) ;
328
337
}
329
338
330
339
export function consumerMarkDirty ( node : ReactiveNode ) : void {
@@ -339,8 +348,13 @@ export function consumerMarkDirty(node: ReactiveNode): void {
339
348
* Must be called by subclasses which represent reactive computations, before those computations
340
349
* begin.
341
350
*/
342
- export function consumerBeforeComputation ( node : ReactiveNode | null ) : ReactiveNode | null {
343
- node && ( node . nextProducerIndex = 0 ) ;
351
+ export function consumerBeforeComputation (
352
+ node : ( ReactiveNode & InteropConsumer ) | null ,
353
+ ) : InteropConsumer | null {
354
+ if ( node ) {
355
+ node . nextProducerIndex = 0 ;
356
+ node . hasInteropSignalDep = false ;
357
+ }
344
358
return setActiveConsumer ( node ) ;
345
359
}
346
360
@@ -352,7 +366,7 @@ export function consumerBeforeComputation(node: ReactiveNode | null): ReactiveNo
352
366
*/
353
367
export function consumerAfterComputation (
354
368
node : ReactiveNode | null ,
355
- prevConsumer : ReactiveNode | null ,
369
+ prevConsumer : InteropConsumer | null ,
356
370
) : void {
357
371
setActiveConsumer ( prevConsumer ) ;
358
372
0 commit comments