Skip to content

Commit bddff73

Browse files
Huxprofacebook-github-bot
authored andcommitted
Introduce drainMicrotasks to JSI
Summary: Changelog: [Internal][Added] - Introduce drainMicrotasks to JSI This diff proposed a new JSI API `drainMicrotasks` to define how hosts may integrate with the JSVMs' internal microtask queue (a.k.a. job queue) and hence their native Promise. The name `drainMicrotasks` is slightly preferred over `drainJobs` to favor *host-friendliness* over *engine-friendliness*. This diff auto implement the new API in JSC and Hermes as stubs to make sure things compiled. Please refer to the doc-comments in the `jsi.h` for a detailed documentation on the semantics and the design of this API. ### Notes on the existing APIs from JSVMs The presence of such queue and APIs to operate on them are ubiquitous: - Hermes: `Runtime::drainJobs` - V8: `MicrotaskQueue::PerformCheckpoint` - JSC: `VM::drainMicrotasks` - QuickJS: `JS_ExecutePendingJob` The only exception is ChakraCore, which requires hosts to provide the queue and set up the `JsSetPromiseContinuationCallback`, but a JSI implementation can provide that queue trivially. ### Extra note on ECMA-262 ECMA-262 changed the spec at Mar 2020 from "asking an ECMAScript implementation to maintain a Job Queue" to "Ask the embedder to define its event loop, including the job queue" so technically: - the internal approach is closer to the old spec. - the ChakraCore approach is closer to the current spec. Reviewed By: mhorowitz Differential Revision: D27727920 fbshipit-source-id: b839b959facbc009f7d14b781e9287c46ea64373
1 parent 266b21b commit bddff73

File tree

3 files changed

+43
-0
lines changed

3 files changed

+43
-0
lines changed

ReactCommon/jsi/JSCRuntime.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ class JSCRuntime : public jsi::Runtime {
5050
jsi::Value evaluateJavaScript(
5151
const std::shared_ptr<const jsi::Buffer> &buffer,
5252
const std::string &sourceURL) override;
53+
54+
bool drainMicrotasks(int maxMicrotasksHint = -1) override;
55+
5356
jsi::Object global() override;
5457

5558
std::string description() override;
@@ -432,6 +435,10 @@ jsi::Value JSCRuntime::evaluateJavaScript(
432435
return createValue(res);
433436
}
434437

438+
bool JSCRuntime::drainMicrotasks(int maxMicrotasksHint) {
439+
return true;
440+
}
441+
435442
jsi::Object JSCRuntime::global() {
436443
return createObject(JSContextGetGlobalObject(ctx_));
437444
}

ReactCommon/jsi/jsi/decorator.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation {
126126
const std::shared_ptr<const PreparedJavaScript>& js) override {
127127
return plain().evaluatePreparedJavaScript(js);
128128
}
129+
bool drainMicrotasks(int maxMicrotasksHint) override {
130+
return plain().drainMicrotasks(maxMicrotasksHint);
131+
}
129132
Object global() override {
130133
return plain().global();
131134
}
@@ -491,6 +494,10 @@ class WithRuntimeDecorator : public RuntimeDecorator<Plain, Base> {
491494
Around around{with_};
492495
return RD::evaluatePreparedJavaScript(js);
493496
}
497+
bool drainMicrotasks(int maxMicrotasksHint) override {
498+
Around around{with_};
499+
return RD::drainMicrotasks(maxMicrotasksHint);
500+
}
494501
Object global() override {
495502
Around around{with_};
496503
return RD::global();

ReactCommon/jsi/jsi/jsi.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,35 @@ class JSI_EXPORT Runtime {
185185
virtual Value evaluatePreparedJavaScript(
186186
const std::shared_ptr<const PreparedJavaScript>& js) = 0;
187187

188+
/// Drain the JavaScript VM internal Microtask (a.k.a. Job in ECMA262) queue.
189+
///
190+
/// \param maxMicrotasksHint a hint to tell an implementation that it should
191+
/// make a best effort not execute more than the given number. It's default
192+
/// to -1 for infinity (unbounded execution).
193+
/// \return true if the queue is drained or false if there is more work to do.
194+
///
195+
/// When there were exceptions thrown from the execution of microtasks,
196+
/// implementations shall discard the exceptional jobs. An implementation may
197+
/// \throw a \c JSError object to signal the hosts to handle. In that case, an
198+
/// implementation may or may not suspend the draining.
199+
///
200+
/// Hosts may call this function again to resume the draining if it was
201+
/// suspended due to either exceptions or the \p maxMicrotasksHint bound.
202+
/// E.g. a host may repetitively invoke this function until the queue is
203+
/// drained to implement the "microtask checkpint" defined in WHATWG HTML
204+
/// event loop: https://html.spec.whatwg.org/C#perform-a-microtask-checkpoint.
205+
///
206+
/// Note that error propagation is only a concern if a host needs to implement
207+
/// `queueMicrotask`, a recent API that allows enqueueing aribitary functions
208+
/// (hence may throw) as microtasks. Exceptions from ECMA-262 Promise Jobs are
209+
/// handled internally to VMs and are never propagrated to hosts.
210+
///
211+
/// This API offers some queue management to hosts at its best effort due to
212+
/// different behaviors and limitations imposed by different VMs and APIs. By
213+
/// the time this is written, An implementation may swallow exceptions (JSC),
214+
/// may not pause (V8), and may not support bounded executions.
215+
virtual bool drainMicrotasks(int maxMicrotasksHint = -1) = 0;
216+
188217
/// \return the global object
189218
virtual Object global() = 0;
190219

0 commit comments

Comments
 (0)