From 6943b2860f4a4d22ca72cc38a67c02f1969da5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Wed, 28 Feb 2024 10:10:11 -0800 Subject: [PATCH] [skip ci] Add queueMicrotask method to JSI Summary: Changelog: [internal] ## Context Microtasks are an important aspect of JavaScript and they will become increasingly important in the hosts where we're currently using JSI. For example, React Native is going to adopt an event loop processing model similar to the one on the Web, which means it would need the ability to schedule and execute microtasks in every iteration of the loop. See https://github.com/react-native-community/discussions-and-proposals/pull/744 for details. JSI already has a method to execute all pending microtasks (`drainMicrotasks`) but without a method to schedule microtasks this is incomplete. We're currently testing microtasks with Hermes using an internal method to schedule microtasks (`HermesInternal.enqueueJob`) but we need a method in JSI so this also works in other runtimes like JSC and V8. ## Changes This adds the `queueMicrotask` to the Runtime API in JSI so we have symmetric API for microtasks and we can implement the necessary functionality. The expectation for JSI implementations is to queue microtasks from this method and from built-ins like Promises and async functions in the same queue, and not drain that queue until explicitly done via `drainMicrotasks` in JSI. This also modifies Hermes and JSC to provide stubs for those methods, and the actual implementation will be done in following diffs. Differential Revision: D54302536 --- packages/react-native/ReactCommon/jsc/JSCRuntime.cpp | 5 ++++- packages/react-native/ReactCommon/jsi/jsi/decorator.h | 7 +++++++ packages/react-native/ReactCommon/jsi/jsi/jsi.h | 5 +++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp b/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp index c42d4f3008e4a6..d4e1a5ea1f7f1f 100644 --- a/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp +++ b/packages/react-native/ReactCommon/jsc/JSCRuntime.cpp @@ -51,6 +51,7 @@ class JSCRuntime : public jsi::Runtime { const std::shared_ptr& buffer, const std::string& sourceURL) override; + void queueMicrotask(const jsi::Function& callback) override; bool drainMicrotasks(int maxMicrotasksHint = -1) override; jsi::Object global() override; @@ -434,7 +435,9 @@ jsi::Value JSCRuntime::evaluateJavaScript( return createValue(res); } -bool JSCRuntime::drainMicrotasks(int maxMicrotasksHint) { +void JSCRuntime::queueMicrotask(const jsi::Function& /*callback*/) {} + +bool JSCRuntime::drainMicrotasks(int /*maxMicrotasksHint*/) { return true; } diff --git a/packages/react-native/ReactCommon/jsi/jsi/decorator.h b/packages/react-native/ReactCommon/jsi/jsi/decorator.h index 7bddd1fad80a52..6f4351a420da69 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/decorator.h +++ b/packages/react-native/ReactCommon/jsi/jsi/decorator.h @@ -126,6 +126,9 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { const std::shared_ptr& js) override { return plain().evaluatePreparedJavaScript(js); } + void queueMicrotask(const jsi::Function& callback) override { + return plain().queueMicrotask(callback); + } bool drainMicrotasks(int maxMicrotasksHint) override { return plain().drainMicrotasks(maxMicrotasksHint); } @@ -544,6 +547,10 @@ class WithRuntimeDecorator : public RuntimeDecorator { Around around{with_}; return RD::evaluatePreparedJavaScript(js); } + void queueMicrotask(const Function& callback) override { + Around around{with_}; + RD::queueMicrotask(callback); + } bool drainMicrotasks(int maxMicrotasksHint) override { Around around{with_}; return RD::drainMicrotasks(maxMicrotasksHint); diff --git a/packages/react-native/ReactCommon/jsi/jsi/jsi.h b/packages/react-native/ReactCommon/jsi/jsi/jsi.h index 962dae93609423..276fdef6c86067 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/jsi.h +++ b/packages/react-native/ReactCommon/jsi/jsi/jsi.h @@ -209,6 +209,11 @@ class JSI_EXPORT Runtime { virtual Value evaluatePreparedJavaScript( const std::shared_ptr& js) = 0; + // Queues a microtask in the JavaScript VM internal Microtask (a.k.a. Job in + // ECMA262) queue, to be executed when the host drains microtasks in + // its event loop implementation. + virtual void queueMicrotask(const jsi::Function& callback) = 0; + /// Drain the JavaScript VM internal Microtask (a.k.a. Job in ECMA262) queue. /// /// \param maxMicrotasksHint a hint to tell an implementation that it should