diff --git a/core/libdeno/binding.cc b/core/libdeno/binding.cc index 291e62f01caa5d..7827cd52288274 100644 --- a/core/libdeno/binding.cc +++ b/core/libdeno/binding.cc @@ -455,6 +455,16 @@ void EvalContext(const v8::FunctionCallbackInfo& args) { args.GetReturnValue().Set(output); } +void QueueMicrotask(const v8::FunctionCallbackInfo& args) { + v8::Isolate* isolate = args.GetIsolate(); + + if (!(args[0]->IsFunction())) { + ThrowInvalidArgument(isolate); + return; + } + isolate->EnqueueMicrotask(args[0].As()); +} + void InitializeContext(v8::Isolate* isolate, v8::Local context) { v8::HandleScope handle_scope(isolate); v8::Context::Scope context_scope(context); @@ -493,6 +503,15 @@ void InitializeContext(v8::Isolate* isolate, v8::Local context) { CHECK(core_val->SetAccessor(context, deno::v8_str("shared"), Shared) .FromJust()); + + // Direct bindings on `window`. + auto queue_microtask_tmpl = + v8::FunctionTemplate::New(isolate, QueueMicrotask); + auto queue_microtask_val = + queue_microtask_tmpl->GetFunction(context).ToLocalChecked(); + CHECK( + global->Set(context, deno::v8_str("queueMicrotask"), queue_microtask_val) + .FromJust()); } void MessageCallback(v8::Local message, diff --git a/core/libdeno/internal.h b/core/libdeno/internal.h index 50e85017ef258d..f3789fcc3ec983 100644 --- a/core/libdeno/internal.h +++ b/core/libdeno/internal.h @@ -156,6 +156,7 @@ void ErrorToJSON(const v8::FunctionCallbackInfo& args); void Shared(v8::Local property, const v8::PropertyCallbackInfo& info); void MessageCallback(v8::Local message, v8::Local data); +void QueueMicrotask(const v8::FunctionCallbackInfo& args); static intptr_t external_references[] = { reinterpret_cast(Print), reinterpret_cast(Recv), @@ -164,6 +165,7 @@ static intptr_t external_references[] = { reinterpret_cast(ErrorToJSON), reinterpret_cast(Shared), reinterpret_cast(MessageCallback), + reinterpret_cast(QueueMicrotask), 0}; static const deno_buf empty_buf = {nullptr, 0}; diff --git a/js/globals.ts b/js/globals.ts index b47cd80f35d8fc..ed7621165ee2c2 100644 --- a/js/globals.ts +++ b/js/globals.ts @@ -89,6 +89,7 @@ window.onload = undefined as undefined | Function; // standard https://www.w3.org/TR/WebCryptoAPI/#crypto-interface as it does not // yet incorporate the SubtleCrypto interface as its "subtle" property. window.crypto = (csprng as unknown) as Crypto; +// window.queueMicrotask added by hand to self-maintained lib.deno_runtime.d.ts // When creating the runtime type library, we use modifications to `window` to // determine what is in the global namespace. When we put a class in the diff --git a/js/globals_test.ts b/js/globals_test.ts index 3085118de02892..42a055087ac3fb 100644 --- a/js/globals_test.ts +++ b/js/globals_test.ts @@ -77,3 +77,29 @@ test(function DenoNamespaceImmutable(): void { // @ts-ignore assert(print === Deno.core.print); }); + +test(async function windowQueueMicrotask(): Promise { + let resolve1: () => void | undefined; + let resolve2: () => void | undefined; + let microtaskDone = false; + const p1 = new Promise( + (res): void => { + resolve1 = (): void => { + microtaskDone = true; + res(); + }; + } + ); + const p2 = new Promise( + (res): void => { + resolve2 = (): void => { + assert(microtaskDone); + res(); + }; + } + ); + window.queueMicrotask(resolve1!); + setTimeout(resolve2!, 0); + await p1; + await p2; +}); diff --git a/js/lib.deno_runtime.d.ts b/js/lib.deno_runtime.d.ts index bbdc1fcb97cb7d..8ece995815b00c 100644 --- a/js/lib.deno_runtime.d.ts +++ b/js/lib.deno_runtime.d.ts @@ -1264,6 +1264,7 @@ declare interface Window { callback: (event: domTypes.Event) => void | null, options?: boolean | domTypes.EventListenerOptions | undefined ) => void; + queueMicrotask: (task: () => void) => void; Deno: typeof Deno; }