From 3b100d077c39e07d365f2d157cdef130d59f4a14 Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Mon, 22 Sep 2025 11:08:51 -0700 Subject: [PATCH 1/2] feat: queueMicrotask support --- NativeScript/runtime/Runtime.mm | 15 ++++++++ .../app/tests/RuntimeImplementedAPIs.js | 34 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/NativeScript/runtime/Runtime.mm b/NativeScript/runtime/Runtime.mm index c943b0b2..fb34da94 100644 --- a/NativeScript/runtime/Runtime.mm +++ b/NativeScript/runtime/Runtime.mm @@ -193,6 +193,21 @@ void DisposeIsolateWhenPossible(Isolate* isolate) { DefinePerformanceObject(isolate, globalTemplate); DefineTimeMethod(isolate, globalTemplate); DefineDrainMicrotaskMethod(isolate, globalTemplate); + // queueMicrotask(callback) per spec + { + Local qmtTemplate = FunctionTemplate::New( + isolate, [](const FunctionCallbackInfo& info) { + auto* isolate = info.GetIsolate(); + if (info.Length() < 1 || !info[0]->IsFunction()) { + isolate->ThrowException(Exception::TypeError( + tns::ToV8String(isolate, "queueMicrotask: callback must be a function"))); + return; + } + Local cb = info[0].As(); + isolate->EnqueueMicrotask(cb); + }); + globalTemplate->Set(tns::ToV8String(isolate, "queueMicrotask"), qmtTemplate); + } ObjectManager::Init(isolate, globalTemplate); // SetTimeout::Init(isolate, globalTemplate); MetadataBuilder::RegisterConstantsOnGlobalObject(isolate, globalTemplate, isWorker); diff --git a/TestRunner/app/tests/RuntimeImplementedAPIs.js b/TestRunner/app/tests/RuntimeImplementedAPIs.js index fe3bf575..b3d581e2 100644 --- a/TestRunner/app/tests/RuntimeImplementedAPIs.js +++ b/TestRunner/app/tests/RuntimeImplementedAPIs.js @@ -44,3 +44,37 @@ describe("Performance object", () => { expect(Math.abs(dateNow - performanceAccurateNow)).toBeLessThan(10); }); }); + +describe("queueMicrotask", () => { + it("should be defined as a function", () => { + expect(typeof queueMicrotask).toBe("function"); + }); + + it("should throw TypeError when callback is not a function", () => { + expect(() => queueMicrotask(null)).toThrow(); + expect(() => queueMicrotask(42)).toThrow(); + expect(() => queueMicrotask({})).toThrow(); + }); + + it("runs after current stack but before setTimeout(0)", (done) => { + const order = []; + queueMicrotask(() => order.push("microtask")); + setTimeout(() => { + order.push("timeout"); + expect(order).toEqual(["microtask", "timeout"]); + done(); + }, 0); + expect(order.length).toBe(0); + }); + + it("preserves ordering with Promise microtasks", (done) => { + const order = []; + queueMicrotask(() => order.push("qm1")); + Promise.resolve().then(() => order.push("p")); + queueMicrotask(() => order.push("qm2")); + setTimeout(() => { + expect(order).toEqual(["qm1", "p", "qm2"]); + done(); + }, 0); + }); +}); From b05e8322a8a89e583d91fd1dca9aca1e1a209f7c Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Mon, 22 Sep 2025 22:45:10 -0700 Subject: [PATCH 2/2] fix: build --- NativeScript/runtime/Runtime.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NativeScript/runtime/Runtime.mm b/NativeScript/runtime/Runtime.mm index fb34da94..f8773f5f 100644 --- a/NativeScript/runtime/Runtime.mm +++ b/NativeScript/runtime/Runtime.mm @@ -203,7 +203,7 @@ void DisposeIsolateWhenPossible(Isolate* isolate) { tns::ToV8String(isolate, "queueMicrotask: callback must be a function"))); return; } - Local cb = info[0].As(); + v8::Local cb = info[0].As(); isolate->EnqueueMicrotask(cb); }); globalTemplate->Set(tns::ToV8String(isolate, "queueMicrotask"), qmtTemplate);