From f82324695d4b6ead6184ed7b5f5334d1a5e4bcbb Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Mon, 22 Sep 2025 11:09:18 -0700 Subject: [PATCH] feat: queueMicrotask support --- test-app/app/src/main/assets/app/mainpage.js | 3 +- .../assets/app/tests/testQueueMicrotask.js | 35 +++++++++++++++++++ test-app/runtime/src/main/cpp/Runtime.cpp | 15 ++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 test-app/app/src/main/assets/app/tests/testQueueMicrotask.js diff --git a/test-app/app/src/main/assets/app/mainpage.js b/test-app/app/src/main/assets/app/mainpage.js index 14a3d0553..4b76cb997 100644 --- a/test-app/app/src/main/assets/app/mainpage.js +++ b/test-app/app/src/main/assets/app/mainpage.js @@ -72,4 +72,5 @@ require("./tests/testPostFrameCallback"); require("./tests/console/logTests.js"); require('./tests/testURLImpl.js'); require('./tests/testURLSearchParamsImpl.js'); -require('./tests/testPerformanceNow'); \ No newline at end of file +require('./tests/testPerformanceNow'); +require('./tests/testQueueMicrotask'); \ No newline at end of file diff --git a/test-app/app/src/main/assets/app/tests/testQueueMicrotask.js b/test-app/app/src/main/assets/app/tests/testQueueMicrotask.js new file mode 100644 index 000000000..6dba7a5f5 --- /dev/null +++ b/test-app/app/src/main/assets/app/tests/testQueueMicrotask.js @@ -0,0 +1,35 @@ +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(123)).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); + // at this point, nothing should have run yet + 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); + }); +}); diff --git a/test-app/runtime/src/main/cpp/Runtime.cpp b/test-app/runtime/src/main/cpp/Runtime.cpp index afbd10eb2..e555e4939 100644 --- a/test-app/runtime/src/main/cpp/Runtime.cpp +++ b/test-app/runtime/src/main/cpp/Runtime.cpp @@ -537,6 +537,21 @@ Isolate* Runtime::PrepareV8Runtime(const string& filesPath, const string& native performanceTemplate->Set(ArgConverter::ConvertToV8String(isolate, "timeOrigin"), Number::New(isolate, m_realtimeOrigin)); globalTemplate->Set(ArgConverter::ConvertToV8String(isolate, "performance"), performanceTemplate); } + // queueMicrotask(callback) per spec: https://developer.mozilla.org/en-US/docs/Web/API/Window/queueMicrotask + globalTemplate->Set( + ArgConverter::ConvertToV8String(isolate, "queueMicrotask"), + FunctionTemplate::New( + isolate, + [](const v8::FunctionCallbackInfo& info) { + auto* isolate = info.GetIsolate(); + if (info.Length() < 1 || !info[0]->IsFunction()) { + isolate->ThrowException(v8::Exception::TypeError( + ArgConverter::ConvertToV8String(isolate, "queueMicrotask: callback must be a function"))); + return; + } + v8::Local cb = info[0].As(); + isolate->EnqueueMicrotask(cb); + })); globalTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__releaseNativeCounterpart"), FunctionTemplate::New(isolate, CallbackHandlers::ReleaseNativeCounterpartCallback)); globalTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__markingMode"), Number::New(isolate, m_objectManager->GetMarkingMode()), readOnlyFlags); globalTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__runOnMainThread"), FunctionTemplate::New(isolate, CallbackHandlers::RunOnMainThreadCallback));