155 lines
4.2 KiB
Objective-C
155 lines
4.2 KiB
Objective-C
// Copyright 2004-present Facebook. All Rights Reserved.
|
|
|
|
#import "RCTTiming.h"
|
|
|
|
#import "RCTBridge.h"
|
|
#import "RCTLog.h"
|
|
#import "RCTModuleIDs.h"
|
|
#import "RCTSparseArray.h"
|
|
#import "RCTUtils.h"
|
|
|
|
@interface RCTTimer : NSObject
|
|
|
|
@property (nonatomic, strong, readonly) NSDate *target;
|
|
@property (nonatomic, assign, readonly, getter=isActive) BOOL active;
|
|
@property (nonatomic, assign, readonly) BOOL repeats;
|
|
@property (nonatomic, strong, readonly) NSNumber *callbackID;
|
|
@property (nonatomic, assign, readonly) NSTimeInterval interval;
|
|
|
|
@end
|
|
|
|
@implementation RCTTimer
|
|
|
|
- (instancetype)initWithCallbackID:(NSNumber *)callbackID
|
|
interval:(NSTimeInterval)interval
|
|
targetTime:(NSTimeInterval)targetTime
|
|
repeats:(BOOL)repeats
|
|
{
|
|
if ((self = [super init])) {
|
|
_active = YES;
|
|
_interval = interval;
|
|
_repeats = repeats;
|
|
_callbackID = callbackID;
|
|
_target = [NSDate dateWithTimeIntervalSinceNow:targetTime];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
/**
|
|
* Returns `YES` if we should invoke the JS callback.
|
|
*/
|
|
- (BOOL)updateFoundNeedsJSUpdate
|
|
{
|
|
if (_active && _target.timeIntervalSinceNow <= 0) {
|
|
// The JS Timers will do fine grained calculating of expired timeouts.
|
|
if (_repeats) {
|
|
_target = [NSDate dateWithTimeIntervalSinceNow:_interval];
|
|
} else {
|
|
_active = NO;
|
|
}
|
|
return YES;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation RCTTiming
|
|
{
|
|
RCTSparseArray *_timers;
|
|
RCTBridge *_bridge;
|
|
}
|
|
|
|
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
|
{
|
|
if ((self = [super init])) {
|
|
_bridge = bridge;
|
|
_timers = [[RCTSparseArray alloc] init];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
/**
|
|
* TODO (#5906496): Wait until operations on `javaScriptQueue` are complete to complete the
|
|
* `dealloc`.
|
|
*/
|
|
/* - (void)dealloc
|
|
{
|
|
} */
|
|
|
|
- (void)enqueueUpdateTimers
|
|
{
|
|
RCTAssertMainThread();
|
|
|
|
NSMutableArray *timersToCall = [[NSMutableArray alloc] init];
|
|
for (RCTTimer *timer in _timers.allObjects) {
|
|
if ([timer updateFoundNeedsJSUpdate]) {
|
|
[timersToCall addObject:timer.callbackID];
|
|
}
|
|
if (!timer.active) {
|
|
_timers[timer.callbackID] = nil;
|
|
}
|
|
}
|
|
|
|
// call timers that need to be called
|
|
if ([timersToCall count] > 0) {
|
|
[_bridge enqueueJSCall:RCTModuleIDJSTimers methodID:RCTJSTimersCallTimers args:@[timersToCall]];
|
|
}
|
|
}
|
|
|
|
- (void)scheduleCallbackID:(NSNumber *)callbackID interval:(NSTimeInterval)interval targetTime:(NSTimeInterval)targetTime repeats:(BOOL)repeats
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
RCTTimer *timer = [[RCTTimer alloc] initWithCallbackID:callbackID interval:interval targetTime:targetTime repeats:repeats];
|
|
_timers[callbackID] = timer;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* There's a small difference between the time when we call
|
|
* setTimeout/setInterval/requestAnimation frame and the time it actually makes
|
|
* it here. This is important and needs to be taken into account when
|
|
* calculating the timer's target time. We calculate this by passing in
|
|
* Date.now() from JS and then subtracting that from the current time here.
|
|
*/
|
|
- (void)createTimer:(NSNumber *)callbackID
|
|
duration:(NSNumber *)jsDuration
|
|
jsSchedulingTime:(NSNumber *)jsSchedulingTime
|
|
repeats:(NSNumber *)repeats
|
|
{
|
|
RCT_EXPORT();
|
|
|
|
NSTimeInterval interval = jsDuration.doubleValue / 1000;
|
|
NSTimeInterval jsCreationTimeSinceUnixEpoch = jsSchedulingTime.doubleValue / 1000;
|
|
NSTimeInterval currentTimeSinceUnixEpoch = [[NSDate date] timeIntervalSince1970];
|
|
NSTimeInterval jsSchedulingOverhead = currentTimeSinceUnixEpoch - jsCreationTimeSinceUnixEpoch;
|
|
if (jsSchedulingOverhead < 0) {
|
|
RCTLogWarn(@"jsSchedulingOverhead (%ims) should be positive", (int)(jsSchedulingOverhead * 1000));
|
|
}
|
|
|
|
NSTimeInterval targetTime = interval - jsSchedulingOverhead;
|
|
if (interval < 0.018) { // Make sure short intervals run each frame
|
|
interval = 0;
|
|
}
|
|
|
|
[self scheduleCallbackID:callbackID
|
|
interval:interval
|
|
targetTime:targetTime
|
|
repeats:repeats.boolValue];
|
|
}
|
|
|
|
- (void)deleteTimer:(NSNumber *)timerID
|
|
{
|
|
RCT_EXPORT();
|
|
|
|
if (timerID) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
_timers[timerID] = nil;
|
|
});
|
|
} else {
|
|
RCTLogWarn(@"Called deleteTimer: with a nil timerID");
|
|
}
|
|
}
|
|
|
|
@end
|