Skip to content

Commit b6f99b9

Browse files
authored
UnwrapIfPromise Call RunAvailableContinuations (#1813)
1 parent 00d6a6e commit b6f99b9

File tree

6 files changed

+67
-10
lines changed

6 files changed

+67
-10
lines changed

Jint.Tests/Runtime/AsyncTests.cs

+50
Original file line numberDiff line numberDiff line change
@@ -228,4 +228,54 @@ async function foo(name) {
228228

229229
Assert.Equal(expected, log.Select(x => x.AsString()).ToArray());
230230
}
231+
232+
[Fact]
233+
public void ShouldPromiseBeResolved()
234+
{
235+
var log = new List<string>();
236+
Engine engine = new();
237+
engine.SetValue("log", (string str) =>
238+
{
239+
log.Add(str);
240+
});
241+
242+
const string Script = """
243+
async function main() {
244+
return new Promise(function (resolve) {
245+
log('Promise!')
246+
resolve(null)
247+
}).then(function () {
248+
log('Resolved!')
249+
});
250+
}
251+
""";
252+
var result = engine.Execute(Script);
253+
var val = result.GetValue("main");
254+
val.Call().UnwrapIfPromise();
255+
Assert.Equal(2, log.Count);
256+
Assert.Equal("Promise!", log[0]);
257+
Assert.Equal("Resolved!", log[1]);
258+
}
259+
260+
[Fact]
261+
public void ShouldPromiseBeResolved2()
262+
{
263+
Engine engine = new();
264+
engine.SetValue("setTimeout",
265+
(Action action, int ms) =>
266+
{
267+
Task.Delay(ms).ContinueWith(_ => action());
268+
});
269+
270+
const string Script = """
271+
var delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
272+
async function main() {
273+
await delay(100);
274+
return 1;
275+
}
276+
""";
277+
var result = engine.Execute(Script);
278+
var val = result.GetValue("main").Call();
279+
Assert.Equal(1, val.UnwrapIfPromise().AsInteger());
280+
}
231281
}

Jint/JsValueExtensions.cs

+12
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,18 @@ public static JsValue UnwrapIfPromise(this JsValue value)
630630
{
631631
if (value is JsPromise promise)
632632
{
633+
var engine = promise.Engine;
634+
var task = promise.TaskCompletionSource.Task;
635+
engine.RunAvailableContinuations();
636+
engine.AddToEventLoop(() =>
637+
{
638+
if (!task.IsCompleted)
639+
{
640+
// Task.Wait has the potential of inlining the task's execution on the current thread; avoid this.
641+
((IAsyncResult) task).AsyncWaitHandle.WaitOne();
642+
}
643+
});
644+
engine.RunAvailableContinuations();
633645
switch (promise.State)
634646
{
635647
case PromiseState.Pending:

Jint/Native/Date/MimeKit.cs

+2
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,9 @@ private static bool TryParseStandardDateFormat(List<DateToken> tokens, byte[] te
454454
return true;
455455
}
456456

457+
#pragma warning disable CA1859
457458
private static bool TryParseUnknownDateFormat(IList<DateToken> tokens, byte[] text, out DateTimeOffset date)
459+
#pragma warning restore CA1859
458460
{
459461
int? day = null, month = null, year = null, tzone = null;
460462
int hour = 0, minute = 0, second = 0;

Jint/Native/JsPromise.cs

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ internal sealed class JsPromise : ObjectInstance
1212

1313
// valid only in settled state (Fulfilled or Rejected)
1414
internal JsValue Value { get; private set; } = null!;
15+
internal TaskCompletionSource<JsPromise> TaskCompletionSource { get; }= new();
1516

1617
internal List<PromiseReaction> PromiseRejectReactions = new();
1718
internal List<PromiseReaction> PromiseFulfillReactions = new();
@@ -126,6 +127,7 @@ private JsValue RejectPromise(JsValue reason)
126127
var reactions = PromiseRejectReactions;
127128
PromiseRejectReactions = new List<PromiseReaction>();
128129
PromiseFulfillReactions.Clear();
130+
TaskCompletionSource.SetCanceled();
129131

130132
// Note that this part is skipped because there is no tracking yet
131133
// 7. If promise.[[PromiseIsHandled]] is false, perform HostPromiseRejectionTracker(promise, "reject").
@@ -145,6 +147,7 @@ private JsValue FulfillPromise(JsValue result)
145147
var reactions = PromiseFulfillReactions;
146148
PromiseFulfillReactions = new List<PromiseReaction>();
147149
PromiseRejectReactions.Clear();
150+
TaskCompletionSource.SetResult(this);
148151

149152
return PromiseOperations.TriggerPromiseReactions(_engine, reactions, result);
150153
}

Jint/Native/JsValue.cs

-9
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,6 @@ internal static JsValue ConvertTaskToPromise(Engine engine, Task task)
178178
}
179179
});
180180

181-
engine.AddToEventLoop(() =>
182-
{
183-
if (!task.IsCompleted)
184-
{
185-
// Task.Wait has the potential of inlining the task's execution on the current thread; avoid this.
186-
((IAsyncResult) task).AsyncWaitHandle.WaitOne();
187-
}
188-
});
189-
190181
return promise;
191182
}
192183

Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs

-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ protected override object EvaluateInternal(EvaluationContext context)
3434
value = promiseInstance;
3535
}
3636

37-
engine.RunAvailableContinuations();
3837
return value.UnwrapIfPromise();
3938
}
4039
catch (PromiseRejectedException e)

0 commit comments

Comments
 (0)