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,7 @@ export const REACTIVE_NODE: ReactiveNode = {
70
61
producerRecomputeValue : ( ) => { } ,
71
62
consumerMarkedDirty : ( ) => { } ,
72
63
consumerOnSignalRead : ( ) => { } ,
64
+ producerValue : ( ) => { } ,
73
65
} ;
74
66
75
67
/**
@@ -143,6 +135,11 @@ export interface ReactiveNode {
143
135
*/
144
136
nextProducerIndex : number ;
145
137
138
+ /**
139
+ * Whether this consumer has any interop signals as dependencies.
140
+ */
141
+ hasInteropSignalDep : boolean ;
142
+
146
143
/**
147
144
* Array of consumers of this producer that are "live" (they require push notifications).
148
145
*
@@ -180,6 +177,17 @@ export interface ReactiveNode {
180
177
*/
181
178
consumerOnSignalRead ( node : unknown ) : void ;
182
179
180
+ /**
181
+ * Return the current value of the producer (may throw an error if it is the cached value).
182
+ * Does not recompute the value.
183
+ */
184
+ producerValue ( ) : unknown ;
185
+
186
+ /**
187
+ * Called when the signal is accessed.
188
+ */
189
+ producerOnAccess ?( ) : void ;
190
+
183
191
/**
184
192
* Called when the signal becomes "live"
185
193
*/
@@ -211,27 +219,32 @@ interface ProducerNode extends ReactiveNode {
211
219
/**
212
220
* Called by implementations when a producer's signal is read.
213
221
*/
214
- export function producerAccessed ( node : ReactiveNode ) : void {
222
+ export function producerAccessed < T > ( node : ReactiveNode & InteropSignal < T > ) : void {
215
223
if ( inNotificationPhase ) {
216
224
throw new Error (
217
225
typeof ngDevMode !== 'undefined' && ngDevMode
218
226
? `Assertion error: signal read during notification phase`
219
227
: '' ,
220
228
) ;
221
229
}
230
+ getActiveConsumer ( ) ?. addProducer ( node ) ;
231
+ }
222
232
223
- if ( activeConsumer === null ) {
224
- // Accessed outside of a reactive context, so nothing to record.
225
- return ;
226
- }
227
-
233
+ export function internalProducerAccessed (
234
+ node : ReactiveNode ,
235
+ activeConsumer : ReactiveNode & InteropConsumer ,
236
+ ) : void {
228
237
activeConsumer . consumerOnSignalRead ( node ) ;
229
238
230
239
// This producer is the `idx`th dependency of `activeConsumer`.
231
240
const idx = activeConsumer . nextProducerIndex ++ ;
232
241
233
242
assertConsumerNode ( activeConsumer ) ;
234
243
244
+ if ( node . hasInteropSignalDep ) {
245
+ activeConsumer . hasInteropSignalDep = true ;
246
+ }
247
+
235
248
if ( idx < activeConsumer . producerNode . length && activeConsumer . producerNode [ idx ] !== node ) {
236
249
// There's been a change in producers since the last execution of `activeConsumer`.
237
250
// `activeConsumer.producerNode[idx]` holds a stale dependency which will be be removed and
@@ -275,7 +288,7 @@ export function producerIncrementEpoch(): void {
275
288
* Ensure this producer's `version` is up-to-date.
276
289
*/
277
290
export function producerUpdateValueVersion ( node : ReactiveNode ) : void {
278
- if ( ! node . dirty && node . lastCleanEpoch === epoch ) {
291
+ if ( ! node . dirty && node . lastCleanEpoch === epoch && ! node . hasInteropSignalDep ) {
279
292
// Even non-live consumers can skip polling if they previously found themselves to be clean at
280
293
// the current epoch, since their dependencies could not possibly have changed (such a change
281
294
// would've increased the epoch).
@@ -324,7 +337,10 @@ export function producerNotifyConsumers(node: ReactiveNode): void {
324
337
* based on the current consumer context.
325
338
*/
326
339
export function producerUpdatesAllowed ( ) : boolean {
327
- return activeConsumer ?. consumerAllowSignalWrites !== false ;
340
+ return (
341
+ ( getActiveConsumer ( ) as ( InteropConsumer & ReactiveNode ) | null ) ?. consumerAllowSignalWrites !==
342
+ false
343
+ ) ;
328
344
}
329
345
330
346
export function consumerMarkDirty ( node : ReactiveNode ) : void {
@@ -339,8 +355,13 @@ export function consumerMarkDirty(node: ReactiveNode): void {
339
355
* Must be called by subclasses which represent reactive computations, before those computations
340
356
* begin.
341
357
*/
342
- export function consumerBeforeComputation ( node : ReactiveNode | null ) : ReactiveNode | null {
343
- node && ( node . nextProducerIndex = 0 ) ;
358
+ export function consumerBeforeComputation (
359
+ node : ( ReactiveNode & InteropConsumer ) | null ,
360
+ ) : InteropConsumer | null {
361
+ if ( node ) {
362
+ node . nextProducerIndex = 0 ;
363
+ node . hasInteropSignalDep = false ;
364
+ }
344
365
return setActiveConsumer ( node ) ;
345
366
}
346
367
@@ -352,7 +373,7 @@ export function consumerBeforeComputation(node: ReactiveNode | null): ReactiveNo
352
373
*/
353
374
export function consumerAfterComputation (
354
375
node : ReactiveNode | null ,
355
- prevConsumer : ReactiveNode | null ,
376
+ prevConsumer : InteropConsumer | null ,
356
377
) : void {
357
378
setActiveConsumer ( prevConsumer ) ;
358
379
0 commit comments