1+ using System . Threading ;
12using Asynkron . JsEngine . Ast ;
23using Asynkron . JsEngine . JsTypes ;
34
@@ -17,15 +18,22 @@ public static bool IsPromiseLike(object? candidate)
1718 thenValue is IJsCallable ;
1819 }
1920
20- public static bool TryAwaitPromiseSync ( object ? candidate , EvaluationContext context , out object ? resolvedValue )
21+ public static bool TryAwaitPromiseSync (
22+ object ? candidate ,
23+ EvaluationContext context ,
24+ out object ? resolvedValue ,
25+ bool drainMicrotasks = true )
2126 {
2227 resolvedValue = candidate ;
2328
24- // Drain any pending microtasks first - this may resolve the promise we're about to await
2529 var engine = context . RealmState ? . Engine ;
26- engine ? . DrainMicrotasks ( ) ;
30+ if ( drainMicrotasks )
31+ {
32+ engine ? . DrainMicrotasks ( ) ;
33+ }
2734
28- if ( candidate is JsPromise jsPromise &&
35+ if ( drainMicrotasks &&
36+ candidate is JsPromise jsPromise &&
2937 jsPromise . TryGetSettled ( out var settledValue , out var isRejected ) )
3038 {
3139 if ( isRejected )
@@ -41,7 +49,8 @@ public static bool TryAwaitPromiseSync(object? candidate, EvaluationContext cont
4149
4250 while ( resolvedValue is JsObject promiseObj && IsPromiseLike ( promiseObj ) )
4351 {
44- if ( promiseObj . TryGetProperty ( "__promise__" , out var internalPromise ) &&
52+ if ( drainMicrotasks &&
53+ promiseObj . TryGetProperty ( "__promise__" , out var internalPromise ) &&
4554 internalPromise is JsPromise promise &&
4655 promise . TryGetSettled ( out var settled , out var rejected ) )
4756 {
@@ -56,7 +65,8 @@ internalPromise is JsPromise promise &&
5665 continue ;
5766 }
5867
59- if ( ! promiseObj . TryGetProperty ( "then" , out var thenValue ) || thenValue is not IJsCallable thenCallable )
68+ if ( ! promiseObj . TryGetProperty ( "then" , out var thenValue ) ||
69+ thenValue is not IJsCallable thenCallable )
6070 {
6171 break ;
6272 }
@@ -99,32 +109,33 @@ internalPromise is JsPromise promise &&
99109 ( bool Success , object ? Value ) awaited ;
100110 try
101111 {
102- // Drain microtasks until the promise settles
103- var maxIterations = 10000 ; // Prevent infinite loops
104- var iterations = 0 ;
105- while ( ! tcs . Task . IsCompleted && iterations ++ < maxIterations )
112+ if ( drainMicrotasks )
106113 {
107- engine ? . DrainMicrotasks ( ) ;
108-
109- // If still not completed after draining, we have an async promise
110- // that requires the event loop - this shouldn't happen for proper
111- // top-level await scenarios
112- if ( ! tcs . Task . IsCompleted )
114+ var iterations = 0 ;
115+ while ( ! tcs . Task . IsCompleted )
113116 {
114- // Try one more drain in case new microtasks were queued
115117 engine ? . DrainMicrotasks ( ) ;
116- }
117- }
118118
119- if ( tcs . Task . IsCompleted )
120- {
121- awaited = tcs . Task . GetAwaiter ( ) . GetResult ( ) ;
122- }
123- else
124- {
125- throw new InvalidOperationException (
126- "Promise did not resolve after draining microtasks. This may indicate an infinite promise chain or external async dependency." ) ;
119+ if ( tcs . Task . IsCompleted )
120+ {
121+ break ;
122+ }
123+
124+ if ( engine is not null )
125+ {
126+ engine . StartEventLoop ( ) ;
127+ engine . DrainEventLoopAsync ( CancellationToken . None ) . GetAwaiter ( ) . GetResult ( ) ;
128+ engine . DrainMicrotasks ( ) ;
129+ }
130+
131+ if ( ++ iterations > 10_000 )
132+ {
133+ throw new InvalidOperationException (
134+ "Promise did not resolve after draining microtasks and the event loop." ) ;
135+ }
136+ }
127137 }
138+ awaited = tcs . Task . GetAwaiter ( ) . GetResult ( ) ;
128139 }
129140 catch ( InvalidOperationException )
130141 {
@@ -164,7 +175,7 @@ public static bool TryAwaitPromiseOrSchedule(object? candidate, bool asyncStepMo
164175 // existing blocking semantics.
165176 if ( ! asyncStepMode )
166177 {
167- return TryAwaitPromiseSync ( candidate , context , out resolvedValue ) ;
178+ return TryAwaitPromiseSync ( candidate , context , out resolvedValue , context . DrainAwaitMicrotasks ) ;
168179 }
169180
170181 // Async-aware mode: if this is a promise-like object, surface it as
0 commit comments