Perform Engine Microtasks in JSIExecutor
Summary: Changelog: [Internal] This diff introduce a helper `performMicrotaskCheckpoint` to repetitively invoke `jsi::Runtime::drainMicrotasks` to exhaust the microtasks queue provided by JS VMs. Please refer to `jsi.h` for more details about the behavior of the API. Conceptually, the checkpoint needs to be performed whenever the JS stack is considered empty. In practice, we can just make a call whenever RN is returned from C++->JS calls. In the current RN, this happened in JSIExecutor: - `::callFunction` => `callFunctionReturnFlushedQueue_-> call` - `::invokeCallback` => `invokeCallbackAndReturnFlushedQueue_-> call` - `::flush` => `flushedQueue_->call` Each of them invoke a bound method on the JS bridge object. Note that `setImmediate` callbacks are executed before they returned. This means immmediates are invoked before engine microtasks. This is okay because the priority between `setImmediate` and engine "microtask" are not defined. (`setImmediate` is non-standard and RN already treat `setImmediate` in a similar priority as microtask for the existing Promise polyfill.) Reviewed By: RSNara Differential Revision: D27729702 fbshipit-source-id: b64b3705d2ff5100075d860c89f03a847369b7ac
This commit is contained in:
Родитель
bddff73790
Коммит
7310847758
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "jsireact/JSIExecutor.h"
|
||||
|
||||
#include <cxxreact/ErrorUtils.h>
|
||||
#include <cxxreact/JSBigString.h>
|
||||
#include <cxxreact/ModuleRegistry.h>
|
||||
#include <cxxreact/ReactMarker.h>
|
||||
|
@ -204,6 +205,30 @@ void JSIExecutor::registerBundle(
|
|||
ReactMarker::REGISTER_JS_SEGMENT_STOP, tag.c_str());
|
||||
}
|
||||
|
||||
// Looping on \c drainMicrotasks until it completes or hits the retries bound.
|
||||
static void performMicrotaskCheckpoint(jsi::Runtime &runtime) {
|
||||
uint8_t retries = 0;
|
||||
// A heuristic number to guard inifinite or absurd numbers of retries.
|
||||
const static unsigned int kRetriesBound = 255;
|
||||
|
||||
while (retries < kRetriesBound) {
|
||||
try {
|
||||
// The default behavior of \c drainMicrotasks is unbounded execution.
|
||||
// We may want to make it bounded in the future.
|
||||
if (runtime.drainMicrotasks()) {
|
||||
break;
|
||||
}
|
||||
} catch (jsi::JSError &error) {
|
||||
handleJSError(runtime, error, true);
|
||||
}
|
||||
retries++;
|
||||
}
|
||||
|
||||
if (retries == kRetriesBound) {
|
||||
throw std::runtime_error("Hits microtasks retries bound.");
|
||||
}
|
||||
}
|
||||
|
||||
void JSIExecutor::callFunction(
|
||||
const std::string &moduleId,
|
||||
const std::string &methodId,
|
||||
|
@ -240,6 +265,8 @@ void JSIExecutor::callFunction(
|
|||
std::runtime_error("Error calling " + moduleId + "." + methodId));
|
||||
}
|
||||
|
||||
performMicrotaskCheckpoint(*runtime_);
|
||||
|
||||
callNativeModules(ret, true);
|
||||
}
|
||||
|
||||
|
@ -259,6 +286,8 @@ void JSIExecutor::invokeCallback(
|
|||
folly::to<std::string>("Error invoking callback ", callbackId)));
|
||||
}
|
||||
|
||||
performMicrotaskCheckpoint(*runtime_);
|
||||
|
||||
callNativeModules(ret, true);
|
||||
}
|
||||
|
||||
|
@ -394,7 +423,9 @@ void JSIExecutor::callNativeModules(const Value &queue, bool isEndOfBatch) {
|
|||
void JSIExecutor::flush() {
|
||||
SystraceSection s("JSIExecutor::flush");
|
||||
if (flushedQueue_) {
|
||||
callNativeModules(flushedQueue_->call(*runtime_), true);
|
||||
Value ret = flushedQueue_->call(*runtime_);
|
||||
performMicrotaskCheckpoint(*runtime_);
|
||||
callNativeModules(ret, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -410,7 +441,9 @@ void JSIExecutor::flush() {
|
|||
// If calls were made, we bind to the JS bridge methods, and use them to
|
||||
// get the pending queue of native calls.
|
||||
bindBridge();
|
||||
callNativeModules(flushedQueue_->call(*runtime_), true);
|
||||
Value ret = flushedQueue_->call(*runtime_);
|
||||
performMicrotaskCheckpoint(*runtime_);
|
||||
callNativeModules(ret, true);
|
||||
} else if (delegate_) {
|
||||
// If we have a delegate, we need to call it; we pass a null list to
|
||||
// callNativeModules, since we know there are no native calls, without
|
||||
|
|
Загрузка…
Ссылка в новой задаче