Summary: Implements `requestIdleCallback` and `cancelIdleCallback` ### Notes Proposed implementation does yet cover all WHATWG eventloop requirements. - Deadline computation is not implemented and is polyfilled by giving each callback `50ms`, rather than it being shared between other idle callbacks. - The requested callbacks are called with lowest priority by the scheduler as of now, but the execution is not as described in the standard. ## Changelog: - [GENERAL] [ADDED] - Implemented `requestIdleCallback` and `cancelIdleCallback` Pull Request resolved: https://github.com/facebook/react-native/pull/44759 Reviewed By: javache, sammy-SC Differential Revision: D58415077 Pulled By: rubennorte fbshipit-source-id: 46189d4e3ca1d353fa6059a904d677c28c61b604
This commit is contained in:
Родитель
9922628032
Коммит
abfadc6083
|
@ -48,6 +48,25 @@ if (global.RN$Bridgeless !== true) {
|
|||
defineLazyTimer('cancelAnimationFrame');
|
||||
defineLazyTimer('requestIdleCallback');
|
||||
defineLazyTimer('cancelIdleCallback');
|
||||
} else if (
|
||||
// TODO remove this condition when bridgeless == modern scheduler everywhere.
|
||||
NativeReactNativeFeatureFlags != null &&
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks -- false positive due to `use` prefix
|
||||
ReactNativeFeatureFlags.useModernRuntimeScheduler()
|
||||
) {
|
||||
polyfillGlobal(
|
||||
'requestIdleCallback',
|
||||
() =>
|
||||
require('../../src/private/webapis/idlecallbacks/specs/NativeIdleCallbacks')
|
||||
.default.requestIdleCallback,
|
||||
);
|
||||
|
||||
polyfillGlobal(
|
||||
'cancelIdleCallback',
|
||||
() =>
|
||||
require('../../src/private/webapis/idlecallbacks/specs/NativeIdleCallbacks')
|
||||
.default.cancelIdleCallback,
|
||||
);
|
||||
}
|
||||
|
||||
// We need to check if the native module is available before accessing the
|
||||
|
|
|
@ -110,6 +110,7 @@ add_react_common_subdir(react/nativemodule/defaults)
|
|||
add_react_common_subdir(react/nativemodule/dom)
|
||||
add_react_common_subdir(react/nativemodule/featureflags)
|
||||
add_react_common_subdir(react/nativemodule/microtasks)
|
||||
add_react_common_subdir(react/nativemodule/idlecallbacks)
|
||||
add_react_common_subdir(jserrorhandler)
|
||||
add_react_common_subdir(react/runtime)
|
||||
add_react_common_subdir(react/runtime/hermes)
|
||||
|
|
|
@ -25,4 +25,5 @@ target_link_libraries(react_newarchdefaults
|
|||
react_nativemodule_dom
|
||||
react_nativemodule_featureflags
|
||||
react_nativemodule_microtasks
|
||||
react_nativemodule_idlecallbacks
|
||||
jsi)
|
||||
|
|
|
@ -23,4 +23,5 @@ target_link_libraries(react_nativemodule_defaults
|
|||
react_nativemodule_dom
|
||||
react_nativemodule_featureflags
|
||||
react_nativemodule_microtasks
|
||||
react_nativemodule_idlecallbacks
|
||||
)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "DefaultTurboModules.h"
|
||||
#include <react/nativemodule/dom/NativeDOM.h>
|
||||
#include <react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h>
|
||||
#include <react/nativemodule/idlecallbacks/NativeIdleCallbacks.h>
|
||||
#include <react/nativemodule/microtasks/NativeMicrotasks.h>
|
||||
|
||||
namespace facebook::react {
|
||||
|
@ -23,6 +24,10 @@ namespace facebook::react {
|
|||
return std::make_shared<NativeMicrotasks>(jsInvoker);
|
||||
}
|
||||
|
||||
if (name == NativeIdleCallbacks::kModuleName) {
|
||||
return std::make_shared<NativeIdleCallbacks>(jsInvoker);
|
||||
}
|
||||
|
||||
if (name == NativeDOM::kModuleName) {
|
||||
return std::make_shared<NativeDOM>(jsInvoker);
|
||||
}
|
||||
|
|
|
@ -47,4 +47,5 @@ Pod::Spec.new do |s|
|
|||
s.dependency "React-domnativemodule"
|
||||
s.dependency "React-featureflagsnativemodule"
|
||||
s.dependency "React-microtasksnativemodule"
|
||||
s.dependency "React-idlecallbacksnativemodule"
|
||||
end
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
set(CMAKE_VERBOSE_MAKEFILE on)
|
||||
|
||||
add_compile_options(
|
||||
-fexceptions
|
||||
-frtti
|
||||
-std=c++20
|
||||
-Wall
|
||||
-Wpedantic
|
||||
-DLOG_TAG=\"ReactNative\")
|
||||
|
||||
file(GLOB react_nativemodule_idlecallbacks_SRC CONFIGURE_DEPENDS *.cpp)
|
||||
add_library(react_nativemodule_idlecallbacks STATIC ${react_nativemodule_idlecallbacks_SRC})
|
||||
|
||||
target_include_directories(react_nativemodule_idlecallbacks PUBLIC ${REACT_COMMON_DIR})
|
||||
|
||||
target_link_libraries(react_nativemodule_idlecallbacks
|
||||
react_codegen_rncore
|
||||
react_cxxreact
|
||||
react_render_runtimescheduler
|
||||
)
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "NativeIdleCallbacks.h"
|
||||
#include <react/renderer/runtimescheduler/RuntimeScheduler.h>
|
||||
#include <react/renderer/runtimescheduler/RuntimeSchedulerBinding.h>
|
||||
#include <react/renderer/runtimescheduler/Task.h>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
|
||||
#ifdef RN_DISABLE_OSS_PLUGIN_HEADER
|
||||
#include "Plugins.h"
|
||||
#endif
|
||||
|
||||
std::shared_ptr<facebook::react::TurboModule> NativeIdleCallbacksModuleProvider(
|
||||
std::shared_ptr<facebook::react::CallInvoker> jsInvoker) {
|
||||
return std::make_shared<facebook::react::NativeIdleCallbacks>(
|
||||
std::move(jsInvoker));
|
||||
}
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
namespace {
|
||||
|
||||
class IdleTaskRef : public jsi::NativeState {
|
||||
public:
|
||||
IdleTaskRef(std::shared_ptr<Task> task) : task(std::move(task)) {}
|
||||
|
||||
std::shared_ptr<Task> task;
|
||||
};
|
||||
|
||||
jsi::Function makeTimeRemainingFunction(
|
||||
jsi::Runtime& runtime,
|
||||
std::shared_ptr<RuntimeScheduler> runtimeScheduler,
|
||||
RuntimeSchedulerTimePoint deadline) {
|
||||
return jsi::Function::createFromHostFunction(
|
||||
runtime,
|
||||
jsi::PropNameID::forAscii(runtime, "timeRemaining"),
|
||||
0,
|
||||
[runtimeScheduler, deadline, expired = false](
|
||||
jsi::Runtime& runtime,
|
||||
const jsi::Value& /* unused */,
|
||||
const jsi::Value* /* unused */,
|
||||
size_t /* unused */) mutable {
|
||||
double remainingTime = 0;
|
||||
|
||||
// No need to access the runtime scheduler if this idle callback expired
|
||||
// already.
|
||||
if (!expired) {
|
||||
if (runtimeScheduler->getShouldYield()) {
|
||||
expired = true;
|
||||
} else {
|
||||
auto now = runtimeScheduler->now();
|
||||
|
||||
remainingTime = std::max(
|
||||
static_cast<double>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
deadline - now)
|
||||
.count()),
|
||||
0.0);
|
||||
|
||||
if (remainingTime == 0) {
|
||||
expired = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return jsi::Value(runtime, remainingTime);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NativeIdleCallbacks::NativeIdleCallbacks(std::shared_ptr<CallInvoker> jsInvoker)
|
||||
: NativeIdleCallbacksCxxSpec(std::move(jsInvoker)) {}
|
||||
|
||||
CallbackHandle NativeIdleCallbacks::requestIdleCallback(
|
||||
jsi::Runtime& runtime,
|
||||
SyncCallback<void(jsi::Object)>&& userCallback,
|
||||
std::optional<NativeRequestIdleCallbackOptions> options) {
|
||||
auto binding = RuntimeSchedulerBinding::getBinding(runtime);
|
||||
auto runtimeScheduler = binding->getRuntimeScheduler();
|
||||
|
||||
// handle timeout parameter
|
||||
std::optional<RuntimeSchedulerTimeout> timeout;
|
||||
std::optional<RuntimeSchedulerTimePoint> expirationTime;
|
||||
|
||||
if (options.has_value() && options.value().timeout.has_value()) {
|
||||
auto userTimeout = (options.value().timeout.value());
|
||||
if (userTimeout > 0) {
|
||||
timeout = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::duration<double, std::milli>(userTimeout));
|
||||
expirationTime = runtimeScheduler->now() + timeout.value();
|
||||
}
|
||||
}
|
||||
|
||||
auto userCallbackShared = std::make_shared<SyncCallback<void(jsi::Object)>>(
|
||||
std::move(userCallback));
|
||||
|
||||
auto wrappedCallback = [runtimeScheduler, expirationTime, userCallbackShared](
|
||||
jsi::Runtime& runtime) -> void {
|
||||
// This implementation gives each idle callback a 50ms deadline, instead of
|
||||
// being shared by all idle callbacks. This is ok because we don't really
|
||||
// have idle periods, and if a higher priority task comes in while we're
|
||||
// executing an idle callback, we don't execute any more idle callbacks and
|
||||
// we interrupt the current one. The general outcome should be the same.
|
||||
|
||||
auto executionStartTime = runtimeScheduler->now();
|
||||
auto deadline = executionStartTime + std::chrono::milliseconds(50);
|
||||
auto didTimeout = expirationTime.has_value()
|
||||
? executionStartTime > expirationTime
|
||||
: false;
|
||||
|
||||
jsi::Object idleDeadline{runtime};
|
||||
idleDeadline.setProperty(runtime, "didTimeout", didTimeout);
|
||||
idleDeadline.setProperty(
|
||||
runtime,
|
||||
"timeRemaining",
|
||||
makeTimeRemainingFunction(runtime, runtimeScheduler, deadline));
|
||||
|
||||
userCallbackShared->call(std::move(idleDeadline));
|
||||
};
|
||||
|
||||
std::shared_ptr<Task> task;
|
||||
if (timeout.has_value()) {
|
||||
task = runtimeScheduler->scheduleIdleTask(
|
||||
std::move(wrappedCallback), timeout.value());
|
||||
} else {
|
||||
task = runtimeScheduler->scheduleIdleTask(std::move(wrappedCallback));
|
||||
}
|
||||
|
||||
if (task == nullptr) {
|
||||
throw jsi::JSError(
|
||||
runtime,
|
||||
"requestIdleCallback is not supported in legacy runtime scheduler");
|
||||
}
|
||||
|
||||
jsi::Object taskHandle{runtime};
|
||||
auto taskNativeState = std::make_shared<IdleTaskRef>(task);
|
||||
taskHandle.setNativeState(runtime, std::move(taskNativeState));
|
||||
|
||||
return taskHandle;
|
||||
}
|
||||
|
||||
void NativeIdleCallbacks::cancelIdleCallback(
|
||||
jsi::Runtime& runtime,
|
||||
jsi::Object handle) {
|
||||
auto binding = RuntimeSchedulerBinding::getBinding(runtime);
|
||||
auto runtimeScheduler = binding->getRuntimeScheduler();
|
||||
|
||||
if (!handle.hasNativeState(runtime)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto taskHandle =
|
||||
std::dynamic_pointer_cast<IdleTaskRef>(handle.getNativeState(runtime));
|
||||
if (!taskHandle) {
|
||||
return;
|
||||
}
|
||||
|
||||
runtimeScheduler->cancelTask(*taskHandle->task);
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if __has_include("rncoreJSI.h") // Cmake headers on Android
|
||||
#include "rncoreJSI.h"
|
||||
#elif __has_include("FBReactNativeSpecJSI.h") // CocoaPod headers on Apple
|
||||
#include "FBReactNativeSpecJSI.h"
|
||||
#else
|
||||
#include <FBReactNativeSpec/FBReactNativeSpecJSI.h>
|
||||
#endif
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
using CallbackHandle = jsi::Object;
|
||||
|
||||
using NativeRequestIdleCallbackOptions =
|
||||
NativeIdleCallbacksRequestIdleCallbackOptions<std::optional<double>>;
|
||||
|
||||
template <>
|
||||
struct Bridging<NativeRequestIdleCallbackOptions>
|
||||
: NativeIdleCallbacksRequestIdleCallbackOptionsBridging<
|
||||
NativeRequestIdleCallbackOptions> {};
|
||||
|
||||
class NativeIdleCallbacks
|
||||
: public NativeIdleCallbacksCxxSpec<NativeIdleCallbacks> {
|
||||
public:
|
||||
NativeIdleCallbacks(std::shared_ptr<CallInvoker> jsInvoker);
|
||||
|
||||
CallbackHandle requestIdleCallback(
|
||||
jsi::Runtime& runtime,
|
||||
SyncCallback<void(jsi::Object)>&& callback,
|
||||
std::optional<NativeRequestIdleCallbackOptions> options);
|
||||
void cancelIdleCallback(jsi::Runtime& runtime, jsi::Object handle);
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
|
@ -0,0 +1,49 @@
|
|||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
#
|
||||
# This source code is licensed under the MIT license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
|
||||
require "json"
|
||||
|
||||
package = JSON.parse(File.read(File.join(__dir__, "..", "..", "..", "..", "package.json")))
|
||||
version = package['version']
|
||||
|
||||
source = { :git => 'https://github.com/facebook/react-native.git' }
|
||||
if version == '1000.0.0'
|
||||
# This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in.
|
||||
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
|
||||
else
|
||||
source[:tag] = "v#{version}"
|
||||
end
|
||||
|
||||
header_search_paths = []
|
||||
|
||||
if ENV['USE_FRAMEWORKS']
|
||||
header_search_paths << "\"$(PODS_TARGET_SRCROOT)/../../..\"" # this is needed to allow the module access its own files
|
||||
end
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "React-idlecallbacksnativemodule"
|
||||
s.version = version
|
||||
s.summary = "React Native idle callbacks native module"
|
||||
s.homepage = "https://reactnative.dev/"
|
||||
s.license = package["license"]
|
||||
s.author = "Meta Platforms, Inc. and its affiliates"
|
||||
s.platforms = min_supported_versions
|
||||
s.source = source
|
||||
s.source_files = "*.{cpp,h}"
|
||||
s.header_dir = "react/nativemodule/idlecallbacks"
|
||||
s.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
|
||||
"HEADER_SEARCH_PATHS" => header_search_paths.join(' '),
|
||||
"DEFINES_MODULE" => "YES" }
|
||||
|
||||
if ENV['USE_FRAMEWORKS']
|
||||
s.module_name = "idlecallbacksnativemodule"
|
||||
s.header_mappings_dir = "../.."
|
||||
end
|
||||
|
||||
install_modules_dependencies(s)
|
||||
|
||||
s.dependency "ReactCommon/turbomodule/core"
|
||||
s.dependency "React-runtimescheduler"
|
||||
end
|
|
@ -53,6 +53,18 @@ std::shared_ptr<Task> RuntimeScheduler::scheduleTask(
|
|||
return runtimeSchedulerImpl_->scheduleTask(priority, std::move(callback));
|
||||
}
|
||||
|
||||
std::shared_ptr<Task> RuntimeScheduler::scheduleIdleTask(
|
||||
jsi::Function&& callback,
|
||||
RuntimeSchedulerTimeout timeout) noexcept {
|
||||
return runtimeSchedulerImpl_->scheduleIdleTask(std::move(callback), timeout);
|
||||
}
|
||||
|
||||
std::shared_ptr<Task> RuntimeScheduler::scheduleIdleTask(
|
||||
RawCallback&& callback,
|
||||
RuntimeSchedulerTimeout timeout) noexcept {
|
||||
return runtimeSchedulerImpl_->scheduleIdleTask(std::move(callback), timeout);
|
||||
}
|
||||
|
||||
bool RuntimeScheduler::getShouldYield() const noexcept {
|
||||
return runtimeSchedulerImpl_->getShouldYield();
|
||||
}
|
||||
|
|
|
@ -10,11 +10,13 @@
|
|||
#include <ReactCommon/RuntimeExecutor.h>
|
||||
#include <react/renderer/consistency/ShadowTreeRevisionConsistencyManager.h>
|
||||
#include <react/renderer/runtimescheduler/RuntimeSchedulerClock.h>
|
||||
#include <react/renderer/runtimescheduler/SchedulerPriorityUtils.h>
|
||||
#include <react/renderer/runtimescheduler/Task.h>
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
using RuntimeSchedulerRenderingUpdate = std::function<void()>;
|
||||
using RuntimeSchedulerTimeout = std::chrono::milliseconds;
|
||||
|
||||
// This is a temporary abstract class for RuntimeScheduler forks to implement
|
||||
// (and use them interchangeably).
|
||||
|
@ -29,6 +31,14 @@ class RuntimeSchedulerBase {
|
|||
virtual std::shared_ptr<Task> scheduleTask(
|
||||
SchedulerPriority priority,
|
||||
RawCallback&& callback) noexcept = 0;
|
||||
virtual std::shared_ptr<Task> scheduleIdleTask(
|
||||
jsi::Function&& callback,
|
||||
RuntimeSchedulerTimeout timeout = timeoutForSchedulerPriority(
|
||||
SchedulerPriority::IdlePriority)) noexcept = 0;
|
||||
virtual std::shared_ptr<Task> scheduleIdleTask(
|
||||
RawCallback&& callback,
|
||||
RuntimeSchedulerTimeout timeout = timeoutForSchedulerPriority(
|
||||
SchedulerPriority::IdlePriority)) noexcept = 0;
|
||||
virtual void cancelTask(Task& task) noexcept = 0;
|
||||
virtual bool getShouldYield() const noexcept = 0;
|
||||
virtual SchedulerPriority getCurrentPriorityLevel() const noexcept = 0;
|
||||
|
@ -86,6 +96,16 @@ class RuntimeScheduler final : RuntimeSchedulerBase {
|
|||
SchedulerPriority priority,
|
||||
RawCallback&& callback) noexcept override;
|
||||
|
||||
std::shared_ptr<Task> scheduleIdleTask(
|
||||
jsi::Function&& callback,
|
||||
RuntimeSchedulerTimeout timeout = timeoutForSchedulerPriority(
|
||||
SchedulerPriority::IdlePriority)) noexcept override;
|
||||
|
||||
std::shared_ptr<Task> scheduleIdleTask(
|
||||
RawCallback&& callback,
|
||||
RuntimeSchedulerTimeout timeout = timeoutForSchedulerPriority(
|
||||
SchedulerPriority::IdlePriority)) noexcept override;
|
||||
|
||||
/*
|
||||
* Cancelled task will never be executed.
|
||||
*
|
||||
|
|
|
@ -61,6 +61,11 @@ RuntimeSchedulerBinding::RuntimeSchedulerBinding(
|
|||
std::shared_ptr<RuntimeScheduler> runtimeScheduler)
|
||||
: runtimeScheduler_(std::move(runtimeScheduler)) {}
|
||||
|
||||
std::shared_ptr<RuntimeScheduler>
|
||||
RuntimeSchedulerBinding::getRuntimeScheduler() noexcept {
|
||||
return runtimeScheduler_;
|
||||
}
|
||||
|
||||
jsi::Value RuntimeSchedulerBinding::get(
|
||||
jsi::Runtime& runtime,
|
||||
const jsi::PropNameID& name) {
|
||||
|
|
|
@ -37,6 +37,11 @@ class RuntimeSchedulerBinding : public jsi::HostObject {
|
|||
static std::shared_ptr<RuntimeSchedulerBinding> getBinding(
|
||||
jsi::Runtime& runtime);
|
||||
|
||||
/*
|
||||
* Returns shared pointer to RuntimeScheduler for use in native modules
|
||||
*/
|
||||
std::shared_ptr<RuntimeScheduler> getRuntimeScheduler() noexcept;
|
||||
|
||||
/*
|
||||
* `jsi::HostObject` specific overloads.
|
||||
*/
|
||||
|
|
|
@ -80,6 +80,24 @@ std::shared_ptr<Task> RuntimeScheduler_Legacy::scheduleTask(
|
|||
return task;
|
||||
}
|
||||
|
||||
std::shared_ptr<Task> RuntimeScheduler_Legacy::scheduleIdleTask(
|
||||
jsi::Function&& /*callback*/,
|
||||
RuntimeSchedulerTimeout /*timeout*/) noexcept {
|
||||
// Idle tasks are not supported on Legacy RuntimeScheduler.
|
||||
// Because the method is `noexcept`, we return `nullptr` here and handle it
|
||||
// on the caller side.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Task> RuntimeScheduler_Legacy::scheduleIdleTask(
|
||||
RawCallback&& /*callback*/,
|
||||
RuntimeSchedulerTimeout /*timeout*/) noexcept {
|
||||
// Idle tasks are not supported on Legacy RuntimeScheduler.
|
||||
// Because the method is `noexcept`, we return `nullptr` here and handle it
|
||||
// on the caller side.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool RuntimeScheduler_Legacy::getShouldYield() const noexcept {
|
||||
return runtimeAccessRequests_ > 0;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,24 @@ class RuntimeScheduler_Legacy final : public RuntimeSchedulerBase {
|
|||
SchedulerPriority priority,
|
||||
RawCallback&& callback) noexcept override;
|
||||
|
||||
/*
|
||||
* Adds a JavaScript callback to the idle queue with the given timeout.
|
||||
* Triggers workloop if needed.
|
||||
*/
|
||||
std::shared_ptr<Task> scheduleIdleTask(
|
||||
jsi::Function&& callback,
|
||||
RuntimeSchedulerTimeout timeout = timeoutForSchedulerPriority(
|
||||
SchedulerPriority::IdlePriority)) noexcept override;
|
||||
|
||||
/*
|
||||
* Adds a custom callback to the idle queue with the given timeout.
|
||||
* Triggers workloop if needed.
|
||||
*/
|
||||
std::shared_ptr<Task> scheduleIdleTask(
|
||||
RawCallback&& callback,
|
||||
RuntimeSchedulerTimeout timeout = timeoutForSchedulerPriority(
|
||||
SchedulerPriority::IdlePriority)) noexcept override;
|
||||
|
||||
/*
|
||||
* Cancelled task will never be executed.
|
||||
*
|
||||
|
|
|
@ -17,6 +17,17 @@
|
|||
|
||||
namespace facebook::react {
|
||||
|
||||
namespace {
|
||||
std::chrono::milliseconds getResolvedTimeoutForIdleTask(
|
||||
std::chrono::milliseconds customTimeout) {
|
||||
return customTimeout <
|
||||
timeoutForSchedulerPriority(SchedulerPriority::IdlePriority)
|
||||
? timeoutForSchedulerPriority(SchedulerPriority::LowPriority) +
|
||||
customTimeout
|
||||
: timeoutForSchedulerPriority(SchedulerPriority::IdlePriority);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
RuntimeScheduler_Modern::RuntimeScheduler_Modern(
|
||||
|
@ -67,6 +78,45 @@ std::shared_ptr<Task> RuntimeScheduler_Modern::scheduleTask(
|
|||
return task;
|
||||
}
|
||||
|
||||
std::shared_ptr<Task> RuntimeScheduler_Modern::scheduleIdleTask(
|
||||
jsi::Function&& callback,
|
||||
RuntimeSchedulerTimeout customTimeout) noexcept {
|
||||
SystraceSection s(
|
||||
"RuntimeScheduler::scheduleIdleTask",
|
||||
"customTimeout",
|
||||
customTimeout.count(),
|
||||
"callbackType",
|
||||
"jsi::Function");
|
||||
|
||||
auto timeout = getResolvedTimeoutForIdleTask(customTimeout);
|
||||
auto expirationTime = now_() + timeout;
|
||||
auto task = std::make_shared<Task>(
|
||||
SchedulerPriority::IdlePriority, std::move(callback), expirationTime);
|
||||
|
||||
scheduleTask(task);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
std::shared_ptr<Task> RuntimeScheduler_Modern::scheduleIdleTask(
|
||||
RawCallback&& callback,
|
||||
RuntimeSchedulerTimeout customTimeout) noexcept {
|
||||
SystraceSection s(
|
||||
"RuntimeScheduler::scheduleIdleTask",
|
||||
"customTimeout",
|
||||
customTimeout.count(),
|
||||
"callbackType",
|
||||
"RawCallback");
|
||||
|
||||
auto expirationTime = now_() + getResolvedTimeoutForIdleTask(customTimeout);
|
||||
auto task = std::make_shared<Task>(
|
||||
SchedulerPriority::IdlePriority, std::move(callback), expirationTime);
|
||||
|
||||
scheduleTask(task);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
bool RuntimeScheduler_Modern::getShouldYield() const noexcept {
|
||||
std::shared_lock lock(schedulingMutex_);
|
||||
|
||||
|
|
|
@ -70,6 +70,24 @@ class RuntimeScheduler_Modern final : public RuntimeSchedulerBase {
|
|||
SchedulerPriority priority,
|
||||
RawCallback&& callback) noexcept override;
|
||||
|
||||
/*
|
||||
* Adds a JavaScript callback to the idle queue with the given timeout.
|
||||
* Triggers workloop if needed.
|
||||
*/
|
||||
std::shared_ptr<Task> scheduleIdleTask(
|
||||
jsi::Function&& callback,
|
||||
RuntimeSchedulerTimeout customTimeout = timeoutForSchedulerPriority(
|
||||
SchedulerPriority::IdlePriority)) noexcept override;
|
||||
|
||||
/*
|
||||
* Adds a custom callback to the idle queue with the given timeout.
|
||||
* Triggers workloop if needed.
|
||||
*/
|
||||
std::shared_ptr<Task> scheduleIdleTask(
|
||||
RawCallback&& callback,
|
||||
RuntimeSchedulerTimeout customTimeout = timeoutForSchedulerPriority(
|
||||
SchedulerPriority::IdlePriority)) noexcept override;
|
||||
|
||||
/*
|
||||
* Cancelled task will never be executed.
|
||||
*
|
||||
|
|
|
@ -122,6 +122,7 @@ def use_react_native! (
|
|||
pod 'React-featureflags', :path => "#{prefix}/ReactCommon/react/featureflags"
|
||||
pod 'React-featureflagsnativemodule', :path => "#{prefix}/ReactCommon/react/nativemodule/featureflags"
|
||||
pod 'React-microtasksnativemodule', :path => "#{prefix}/ReactCommon/react/nativemodule/microtasks"
|
||||
pod 'React-idlecallbacksnativemodule', :path => "#{prefix}/ReactCommon/react/nativemodule/idlecallbacks"
|
||||
pod 'React-domnativemodule', :path => "#{prefix}/ReactCommon/react/nativemodule/dom"
|
||||
pod 'React-defaultsnativemodule', :path => "#{prefix}/ReactCommon/react/nativemodule/defaults"
|
||||
pod 'React-Mapbuffer', :path => "#{prefix}/ReactCommon"
|
||||
|
|
34
packages/react-native/src/private/webapis/idlecallbacks/specs/NativeIdleCallbacks.js
поставляемый
Normal file
34
packages/react-native/src/private/webapis/idlecallbacks/specs/NativeIdleCallbacks.js
поставляемый
Normal file
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {TurboModule} from '../../../../../Libraries/TurboModule/RCTExport';
|
||||
|
||||
import * as TurboModuleRegistry from '../../../../../Libraries/TurboModule/TurboModuleRegistry';
|
||||
|
||||
export type RequestIdleCallbackOptions = {
|
||||
timeout?: number,
|
||||
};
|
||||
|
||||
export type IdleDeadline = {
|
||||
didTimeout: boolean,
|
||||
timeRemaining: () => mixed,
|
||||
};
|
||||
|
||||
export interface Spec extends TurboModule {
|
||||
+requestIdleCallback: (
|
||||
callback: (idleDeadline: IdleDeadline) => mixed,
|
||||
options?: RequestIdleCallbackOptions,
|
||||
) => mixed;
|
||||
+cancelIdleCallback: (handle: mixed) => void;
|
||||
}
|
||||
|
||||
export default (TurboModuleRegistry.getEnforcing<Spec>(
|
||||
'NativeIdleCallbacksCxx',
|
||||
): Spec);
|
|
@ -86,7 +86,7 @@ class RequestIdleCallbackTester extends React.Component<
|
|||
message = 'Burned CPU for 10ms,';
|
||||
}
|
||||
this.setState({
|
||||
message: `${message} ${deadline.timeRemaining()}ms remaining in frame`,
|
||||
message: `${message} ${deadline.timeRemaining()}ms remaining in frame (timeout: ${String(deadline.didTimeout)})`,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче