@@ -23,6 +23,8 @@ import {
2323 set_dev_stack
2424} from '../../context.js' ;
2525import { flushSync , is_flushing_sync } from '../../reactivity/batch.js' ;
26+ import { BranchManager } from './branches.js' ;
27+ import { capture , unset_context } from '../../reactivity/async.js' ;
2628
2729const PENDING = 0 ;
2830const THEN = 1 ;
@@ -33,7 +35,7 @@ const CATCH = 2;
3335/**
3436 * @template V
3537 * @param {TemplateNode } node
36- * @param {(() => Promise<V> ) } get_input
38+ * @param {(() => any ) } get_input
3739 * @param {null | ((anchor: Node) => void) } pending_fn
3840 * @param {null | ((anchor: Node, value: Source<V>) => void) } then_fn
3941 * @param {null | ((anchor: Node, error: unknown) => void) } catch_fn
@@ -44,161 +46,102 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) {
4446 hydrate_next ( ) ;
4547 }
4648
47- var anchor = node ;
4849 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 ;
6650
6751 var input_source = runes
6852 ? source ( /** @type {V } */ ( undefined ) )
6953 : mutable_source ( /** @type {V } */ ( undefined ) , false , false ) ;
7054 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- }
8855
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- }
56+ var branches = new BranchManager ( node ) ;
12257
123- set_component_context ( null ) ;
124- set_active_reaction ( null ) ;
125- set_active_effect ( null ) ;
126-
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- }
133-
134- var effect = block ( ( ) => {
135- if ( input === ( input = get_input ( ) ) ) return ;
58+ block ( ( ) => {
59+ var input = get_input ( ) ;
60+ var destroyed = false ;
13661
13762 /** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
13863 // @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 ) ;
64+ let mismatch = hydrating && is_promise ( input ) === ( node . data === HYDRATION_START_ELSE ) ;
14065
14166 if ( mismatch ) {
14267 // Hydration mismatch: remove everything inside the anchor and start fresh
143- anchor = skip_nodes ( ) ;
144-
145- set_hydrate_node ( anchor ) ;
68+ set_hydrate_node ( skip_nodes ( ) ) ;
14669 set_hydrating ( false ) ;
14770 mismatch = true ;
14871 }
14972
15073 if ( is_promise ( input ) ) {
151- var promise = input ;
74+ var restore = capture ( ) ;
75+ var resolved = false ;
76+
77+ /**
78+ * @param {() => void } fn
79+ */
80+ const resolve = ( fn ) => {
81+ if ( destroyed ) return ;
82+
83+ resolved = true ;
84+ restore ( ) ;
85+
86+ if ( hydrating ) {
87+ // we want to restore everything _except_ this
88+ set_hydrating ( false ) ;
89+ }
90+
91+ try {
92+ fn ( ) ;
93+ } finally {
94+ unset_context ( ) ;
15295
153- resolved = false ;
96+ // without this, the DOM does not update until two ticks after the promise
97+ // resolves, which is unexpected behaviour (and somewhat irksome to test)
98+ if ( ! is_flushing_sync ) flushSync ( ) ;
99+ }
100+ } ;
154101
155- promise . then (
102+ input . then (
156103 ( 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 ) ;
104+ resolve ( ( ) => {
105+ internal_set ( input_source , value ) ;
106+ branches . ensure ( THEN , then_fn && ( ( target ) => then_fn ( target , input_source ) ) ) ;
107+ } ) ;
162108 } ,
163109 ( 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- }
110+ resolve ( ( ) => {
111+ internal_set ( error_source , error ) ;
112+ branches . ensure ( THEN , catch_fn && ( ( target ) => catch_fn ( target , error_source ) ) ) ;
113+
114+ if ( ! catch_fn ) {
115+ // Rethrow the error if no catch block exists
116+ throw error_source . v ;
117+ }
118+ } ) ;
173119 }
174120 ) ;
175121
176122 if ( hydrating ) {
177- if ( pending_fn ) {
178- pending_effect = branch ( ( ) => pending_fn ( anchor ) ) ;
179- }
123+ branches . ensure ( PENDING , pending_fn ) ;
180124 } else {
181125 // Wait a microtask before checking if we should show the pending state as
182- // the promise might have resolved by the next microtask.
126+ // the promise might have resolved by then
183127 queue_micro_task ( ( ) => {
184- if ( ! resolved ) update ( PENDING , true ) ;
128+ if ( ! resolved ) {
129+ resolve ( ( ) => {
130+ branches . ensure ( PENDING , pending_fn ) ;
131+ } ) ;
132+ }
185133 } ) ;
186134 }
187135 } else {
188136 internal_set ( input_source , input ) ;
189- update ( THEN , false ) ;
137+ branches . ensure ( THEN , then_fn && ( ( target ) => then_fn ( target , input_source ) ) ) ;
190138 }
191139
192140 if ( mismatch ) {
193141 // continue in hydration mode
194142 set_hydrating ( true ) ;
195143 }
196144
197- // Set the input to something else, in order to disable the promise callbacks
198- return ( ) => ( input = UNINITIALIZED ) ;
145+ return ( ) => ( destroyed = true ) ;
199146 } ) ;
200-
201- if ( hydrating ) {
202- anchor = hydrate_node ;
203- }
204147}
0 commit comments