Fix order of timers called in the same frame
Reviewed By: mmmulani Differential Revision: D4802858 fbshipit-source-id: 8d8400c20b7e487aea5a0943f91ac7adc2d23108
This commit is contained in:
parent
ecf4c48966
commit
e0bd35f76f
|
@ -82,17 +82,17 @@ var TimersTest = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
testClearMulti() {
|
testClearMulti() {
|
||||||
var fails = [this.setTimeout(() => this._fail('testClearMulti-1'), 20)];
|
var fails = [];
|
||||||
|
fails.push(this.setTimeout(() => this._fail('testClearMulti-1'), 20));
|
||||||
fails.push(this.setTimeout(() => this._fail('testClearMulti-2'), 50));
|
fails.push(this.setTimeout(() => this._fail('testClearMulti-2'), 50));
|
||||||
var delayClear = this.setTimeout(() => this._fail('testClearMulti-3'), 50);
|
var delayClear = this.setTimeout(() => this._fail('testClearMulti-3'), 50);
|
||||||
fails.push(this.setTimeout(() => this._fail('testClearMulti-4'), 0));
|
fails.push(this.setTimeout(() => this._fail('testClearMulti-4'), 0));
|
||||||
|
|
||||||
this.setTimeout(this.testOrdering, 100); // Next test interleaved
|
|
||||||
|
|
||||||
fails.push(this.setTimeout(() => this._fail('testClearMulti-5'), 10));
|
fails.push(this.setTimeout(() => this._fail('testClearMulti-5'), 10));
|
||||||
|
|
||||||
fails.forEach((timeout) => this.clearTimeout(timeout));
|
fails.forEach((timeout) => this.clearTimeout(timeout));
|
||||||
this.setTimeout(() => this.clearTimeout(delayClear), 20);
|
this.setTimeout(() => this.clearTimeout(delayClear), 20);
|
||||||
|
|
||||||
|
this.setTimeout(this.testOrdering, 50);
|
||||||
},
|
},
|
||||||
|
|
||||||
testOrdering() {
|
testOrdering() {
|
||||||
|
@ -110,14 +110,14 @@ var TimersTest = React.createClass({
|
||||||
() => this._fail('testOrdering-Anim, setTimeout 0 should happen before ' +
|
() => this._fail('testOrdering-Anim, setTimeout 0 should happen before ' +
|
||||||
'requestAnimationFrame')
|
'requestAnimationFrame')
|
||||||
);
|
);
|
||||||
var fail50;
|
var fail25;
|
||||||
this.setTimeout(() => this.clearTimeout(fail50), 20);
|
this.setTimeout(() => { this.clearTimeout(fail25); }, 20);
|
||||||
fail50 = this.setTimeout(
|
fail25 = this.setTimeout(
|
||||||
() => this._fail('testOrdering-t50, setTimeout 20 should happen before ' +
|
() => this._fail('testOrdering-t25, setTimeout 20 should happen before ' +
|
||||||
'setTimeout 50'),
|
'setTimeout 25'),
|
||||||
50
|
25
|
||||||
);
|
);
|
||||||
this.setTimeout(this.done, 75);
|
this.setTimeout(this.done, 50);
|
||||||
},
|
},
|
||||||
|
|
||||||
done() {
|
done() {
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
#import "RCTTiming.h"
|
#import "RCTTiming.h"
|
||||||
|
|
||||||
#import "RCTAssert.h"
|
#import "RCTAssert.h"
|
||||||
#import "RCTBridge.h"
|
|
||||||
#import "RCTBridge+Private.h"
|
#import "RCTBridge+Private.h"
|
||||||
|
#import "RCTBridge.h"
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
|
@ -51,16 +51,20 @@ static const NSTimeInterval kIdleCallbackFrameDeadline = 0.001;
|
||||||
/**
|
/**
|
||||||
* Returns `YES` if we should invoke the JS callback.
|
* Returns `YES` if we should invoke the JS callback.
|
||||||
*/
|
*/
|
||||||
- (BOOL)updateFoundNeedsJSUpdate
|
- (BOOL)shouldFire:(NSDate *)now
|
||||||
{
|
{
|
||||||
if (_target && _target.timeIntervalSinceNow <= 0) {
|
if (_target && [_target timeIntervalSinceDate:now] <= 0) {
|
||||||
// The JS Timers will do fine grained calculating of expired timeouts.
|
|
||||||
_target = _repeats ? [NSDate dateWithTimeIntervalSinceNow:_interval] : nil;
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)reschedule
|
||||||
|
{
|
||||||
|
// The JS Timers will do fine grained calculating of expired timeouts.
|
||||||
|
_target = [NSDate dateWithTimeIntervalSinceNow:_interval];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface _RCTTimingProxy : NSObject
|
@interface _RCTTimingProxy : NSObject
|
||||||
|
@ -178,13 +182,11 @@ RCT_EXPORT_MODULE()
|
||||||
- (void)didUpdateFrame:(RCTFrameUpdate *)update
|
- (void)didUpdateFrame:(RCTFrameUpdate *)update
|
||||||
{
|
{
|
||||||
NSDate *nextScheduledTarget = [NSDate distantFuture];
|
NSDate *nextScheduledTarget = [NSDate distantFuture];
|
||||||
NSMutableArray<NSNumber *> *timersToCall = [NSMutableArray new];
|
NSMutableArray<_RCTTimer *> *timersToCall = [NSMutableArray new];
|
||||||
|
NSDate *now = [NSDate date]; // compare all the timers to the same base time
|
||||||
for (_RCTTimer *timer in _timers.allValues) {
|
for (_RCTTimer *timer in _timers.allValues) {
|
||||||
if ([timer updateFoundNeedsJSUpdate]) {
|
if ([timer shouldFire:now]) {
|
||||||
[timersToCall addObject:timer.callbackID];
|
[timersToCall addObject:timer];
|
||||||
}
|
|
||||||
if (!timer.target) {
|
|
||||||
[_timers removeObjectForKey:timer.callbackID];
|
|
||||||
} else {
|
} else {
|
||||||
nextScheduledTarget = [nextScheduledTarget earlierDate:timer.target];
|
nextScheduledTarget = [nextScheduledTarget earlierDate:timer.target];
|
||||||
}
|
}
|
||||||
|
@ -192,12 +194,24 @@ RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
// Call timers that need to be called
|
// Call timers that need to be called
|
||||||
if (timersToCall.count > 0) {
|
if (timersToCall.count > 0) {
|
||||||
|
NSArray<NSNumber *> *sortedTimers = [[timersToCall sortedArrayUsingComparator:^(_RCTTimer *a, _RCTTimer *b) {
|
||||||
|
return [a.target compare:b.target];
|
||||||
|
}] valueForKey:@"callbackID"];
|
||||||
[_bridge enqueueJSCall:@"JSTimersExecution"
|
[_bridge enqueueJSCall:@"JSTimersExecution"
|
||||||
method:@"callTimers"
|
method:@"callTimers"
|
||||||
args:@[timersToCall]
|
args:@[sortedTimers]
|
||||||
completion:NULL];
|
completion:NULL];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (_RCTTimer *timer in timersToCall) {
|
||||||
|
if (timer.repeats) {
|
||||||
|
[timer reschedule];
|
||||||
|
nextScheduledTarget = [nextScheduledTarget earlierDate:timer.target];
|
||||||
|
} else {
|
||||||
|
[_timers removeObjectForKey:timer.callbackID];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_sendIdleEvents) {
|
if (_sendIdleEvents) {
|
||||||
NSTimeInterval frameElapsed = (CACurrentMediaTime() - update.timestamp);
|
NSTimeInterval frameElapsed = (CACurrentMediaTime() - update.timestamp);
|
||||||
if (kFrameDuration - frameElapsed >= kIdleCallbackFrameDeadline) {
|
if (kFrameDuration - frameElapsed >= kIdleCallbackFrameDeadline) {
|
||||||
|
|
Loading…
Reference in New Issue