1- /** @import { Effect, Source, TemplateNode } from '#client' */
2- import { DEV } from 'esm-env' ;
1+ /** @import { Source, TemplateNode } from '#client' */
32import { is_promise } from '../../../shared/utils.js' ;
4- import { block , branch , pause_effect , resume_effect } from '../../reactivity/effects.js' ;
3+ import { block } from '../../reactivity/effects.js' ;
54import { internal_set , mutable_source , source } from '../../reactivity/sources.js' ;
6- import { set_active_effect , set_active_reaction } from '../../runtime.js' ;
75import {
86 hydrate_next ,
9- hydrate_node ,
107 hydrating ,
118 skip_nodes ,
129 set_hydrate_node ,
1310 set_hydrating
1411} from '../hydration.js' ;
1512import { queue_micro_task } from '../task.js' ;
1613import { HYDRATION_START_ELSE , UNINITIALIZED } from '../../../../constants.js' ;
17- import {
18- component_context ,
19- dev_stack ,
20- is_runes ,
21- set_component_context ,
22- set_dev_current_component_function ,
23- set_dev_stack
24- } from '../../context.js' ;
14+ import { is_runes } from '../../context.js' ;
2515import { flushSync , is_flushing_sync } from '../../reactivity/batch.js' ;
16+ import { BranchManager } from './branches.js' ;
17+ import { capture , unset_context } from '../../reactivity/async.js' ;
2618
2719const PENDING = 0 ;
2820const THEN = 1 ;
@@ -33,7 +25,7 @@ const CATCH = 2;
3325/**
3426 * @template V
3527 * @param {TemplateNode } node
36- * @param {(() => Promise<V> ) } get_input
28+ * @param {(() => any ) } get_input
3729 * @param {null | ((anchor: Node) => void) } pending_fn
3830 * @param {null | ((anchor: Node, value: Source<V>) => void) } then_fn
3931 * @param {null | ((anchor: Node, error: unknown) => void) } catch_fn
@@ -44,161 +36,103 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) {
4436 hydrate_next ( ) ;
4537 }
4638
47- var anchor = node ;
4839 var runes = is_runes ( ) ;
49- var active_component_context = component_context ;
50-
51- /** @type {any } */
52- var component_function = DEV ? component_context ?. function : null ;
53- var dev_original_stack = DEV ? dev_stack : null ;
54-
55- /** @type {V | Promise<V> | typeof UNINITIALIZED } */
56- var input = UNINITIALIZED ;
57-
58- /** @type {Effect | null } */
59- var pending_effect ;
60-
61- /** @type {Effect | null } */
62- var then_effect ;
63-
64- /** @type {Effect | null } */
65- var catch_effect ;
66-
67- var input_source = runes
68- ? source ( /** @type {V } */ ( undefined ) )
69- : mutable_source ( /** @type {V } */ ( undefined ) , false , false ) ;
70- var error_source = runes ? source ( undefined ) : mutable_source ( undefined , false , false ) ;
71- var resolved = false ;
72- /**
73- * @param {AwaitState } state
74- * @param {boolean } restore
75- */
76- function update ( state , restore ) {
77- resolved = true ;
78-
79- if ( restore ) {
80- set_active_effect ( effect ) ;
81- set_active_reaction ( effect ) ; // TODO do we need both?
82- set_component_context ( active_component_context ) ;
83- if ( DEV ) {
84- set_dev_current_component_function ( component_function ) ;
85- set_dev_stack ( dev_original_stack ) ;
86- }
87- }
88-
89- try {
90- if ( state === PENDING && pending_fn ) {
91- if ( pending_effect ) resume_effect ( pending_effect ) ;
92- else pending_effect = branch ( ( ) => pending_fn ( anchor ) ) ;
93- }
94-
95- if ( state === THEN && then_fn ) {
96- if ( then_effect ) resume_effect ( then_effect ) ;
97- else then_effect = branch ( ( ) => then_fn ( anchor , input_source ) ) ;
98- }
99-
100- if ( state === CATCH && catch_fn ) {
101- if ( catch_effect ) resume_effect ( catch_effect ) ;
102- else catch_effect = branch ( ( ) => catch_fn ( anchor , error_source ) ) ;
103- }
104-
105- if ( state !== PENDING && pending_effect ) {
106- pause_effect ( pending_effect , ( ) => ( pending_effect = null ) ) ;
107- }
108-
109- if ( state !== THEN && then_effect ) {
110- pause_effect ( then_effect , ( ) => ( then_effect = null ) ) ;
111- }
112-
113- if ( state !== CATCH && catch_effect ) {
114- pause_effect ( catch_effect , ( ) => ( catch_effect = null ) ) ;
115- }
116- } finally {
117- if ( restore ) {
118- if ( DEV ) {
119- set_dev_current_component_function ( null ) ;
120- set_dev_stack ( null ) ;
121- }
12240
123- set_component_context ( null ) ;
124- set_active_reaction ( null ) ;
125- set_active_effect ( null ) ;
41+ var v = /** @type { V } */ ( UNINITIALIZED ) ;
42+ var value = runes ? source ( v ) : mutable_source ( v , false , false ) ;
43+ var error = runes ? source ( v ) : mutable_source ( v , false , false ) ;
12644
127- // without this, the DOM does not update until two ticks after the promise
128- // resolves, which is unexpected behaviour (and somewhat irksome to test)
129- if ( ! is_flushing_sync ) flushSync ( ) ;
130- }
131- }
132- }
45+ var branches = new BranchManager ( node ) ;
13346
134- var effect = block ( ( ) => {
135- if ( input === ( input = get_input ( ) ) ) return ;
47+ block ( ( ) => {
48+ var input = get_input ( ) ;
49+ var destroyed = false ;
13650
13751 /** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
138- // @ts -ignore coercing `anchor ` to a `Comment` causes TypeScript and Prettier to fight
139- let mismatch = hydrating && is_promise ( input ) === ( anchor . data === HYDRATION_START_ELSE ) ;
52+ // @ts -ignore coercing `node ` to a `Comment` causes TypeScript and Prettier to fight
53+ let mismatch = hydrating && is_promise ( input ) === ( node . data === HYDRATION_START_ELSE ) ;
14054
14155 if ( mismatch ) {
14256 // Hydration mismatch: remove everything inside the anchor and start fresh
143- anchor = skip_nodes ( ) ;
144-
145- set_hydrate_node ( anchor ) ;
57+ set_hydrate_node ( skip_nodes ( ) ) ;
14658 set_hydrating ( false ) ;
147- mismatch = true ;
14859 }
14960
15061 if ( is_promise ( input ) ) {
151- var promise = input ;
62+ var restore = capture ( ) ;
63+ var resolved = false ;
64+
65+ /**
66+ * @param {() => void } fn
67+ */
68+ const resolve = ( fn ) => {
69+ if ( destroyed ) return ;
70+
71+ resolved = true ;
72+ restore ( ) ;
73+
74+ if ( hydrating ) {
75+ // `restore()` could set `hydrating` to `true`, which we very much
76+ // don't want — we want to restore everything _except_ this
77+ set_hydrating ( false ) ;
78+ }
15279
153- resolved = false ;
80+ try {
81+ fn ( ) ;
82+ } finally {
83+ unset_context ( ) ;
15484
155- promise . then (
156- ( value ) => {
157- if ( promise !== input ) return ;
158- // we technically could use `set` here since it's on the next microtick
159- // but let's use internal_set for consistency and just to be safe
160- internal_set ( input_source , value ) ;
161- update ( THEN , true ) ;
85+ // without this, the DOM does not update until two ticks after the promise
86+ // resolves, which is unexpected behaviour (and somewhat irksome to test)
87+ if ( ! is_flushing_sync ) flushSync ( ) ;
88+ }
89+ } ;
90+
91+ input . then (
92+ ( v ) => {
93+ resolve ( ( ) => {
94+ internal_set ( value , v ) ;
95+ branches . ensure ( THEN , then_fn && ( ( target ) => then_fn ( target , value ) ) ) ;
96+ } ) ;
16297 } ,
163- ( error ) => {
164- if ( promise !== input ) return ;
165- // we technically could use `set` here since it's on the next microtick
166- // but let's use internal_set for consistency and just to be safe
167- internal_set ( error_source , error ) ;
168- update ( CATCH , true ) ;
169- if ( ! catch_fn ) {
170- // Rethrow the error if no catch block exists
171- throw error_source . v ;
172- }
98+ ( e ) => {
99+ resolve ( ( ) => {
100+ internal_set ( error , e ) ;
101+ branches . ensure ( THEN , catch_fn && ( ( target ) => catch_fn ( target , error ) ) ) ;
102+
103+ if ( ! catch_fn ) {
104+ // Rethrow the error if no catch block exists
105+ throw error . v ;
106+ }
107+ } ) ;
173108 }
174109 ) ;
175110
176111 if ( hydrating ) {
177- if ( pending_fn ) {
178- pending_effect = branch ( ( ) => pending_fn ( anchor ) ) ;
179- }
112+ branches . ensure ( PENDING , pending_fn ) ;
180113 } else {
181114 // Wait a microtask before checking if we should show the pending state as
182- // the promise might have resolved by the next microtask.
115+ // the promise might have resolved by then
183116 queue_micro_task ( ( ) => {
184- if ( ! resolved ) update ( PENDING , true ) ;
117+ if ( ! resolved ) {
118+ resolve ( ( ) => {
119+ branches . ensure ( PENDING , pending_fn ) ;
120+ } ) ;
121+ }
185122 } ) ;
186123 }
187124 } else {
188- internal_set ( input_source , input ) ;
189- update ( THEN , false ) ;
125+ internal_set ( value , input ) ;
126+ branches . ensure ( THEN , then_fn && ( ( target ) => then_fn ( target , value ) ) ) ;
190127 }
191128
192129 if ( mismatch ) {
193130 // continue in hydration mode
194131 set_hydrating ( true ) ;
195132 }
196133
197- // Set the input to something else, in order to disable the promise callbacks
198- return ( ) => ( input = UNINITIALIZED ) ;
134+ return ( ) => {
135+ destroyed = true ;
136+ } ;
199137 } ) ;
200-
201- if ( hydrating ) {
202- anchor = hydrate_node ;
203- }
204138}
0 commit comments