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() {
|
||||
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));
|
||||
var delayClear = this.setTimeout(() => this._fail('testClearMulti-3'), 50);
|
||||
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.forEach((timeout) => this.clearTimeout(timeout));
|
||||
this.setTimeout(() => this.clearTimeout(delayClear), 20);
|
||||
|
||||
this.setTimeout(this.testOrdering, 50);
|
||||
},
|
||||
|
||||
testOrdering() {
|
||||
|
@ -110,14 +110,14 @@ var TimersTest = React.createClass({
|
|||
() => this._fail('testOrdering-Anim, setTimeout 0 should happen before ' +
|
||||
'requestAnimationFrame')
|
||||
);
|
||||
var fail50;
|
||||
this.setTimeout(() => this.clearTimeout(fail50), 20);
|
||||
fail50 = this.setTimeout(
|
||||
() => this._fail('testOrdering-t50, setTimeout 20 should happen before ' +
|
||||
'setTimeout 50'),
|
||||
50
|
||||
var fail25;
|
||||
this.setTimeout(() => { this.clearTimeout(fail25); }, 20);
|
||||
fail25 = this.setTimeout(
|
||||
() => this._fail('testOrdering-t25, setTimeout 20 should happen before ' +
|
||||
'setTimeout 25'),
|
||||
25
|
||||
);
|
||||
this.setTimeout(this.done, 75);
|
||||
this.setTimeout(this.done, 50);
|
||||
},
|
||||
|
||||
done() {
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#import "RCTTiming.h"
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTBridge+Private.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
|
@ -51,16 +51,20 @@ static const NSTimeInterval kIdleCallbackFrameDeadline = 0.001;
|
|||
/**
|
||||
* Returns `YES` if we should invoke the JS callback.
|
||||
*/
|
||||
- (BOOL)updateFoundNeedsJSUpdate
|
||||
- (BOOL)shouldFire:(NSDate *)now
|
||||
{
|
||||
if (_target && _target.timeIntervalSinceNow <= 0) {
|
||||
// The JS Timers will do fine grained calculating of expired timeouts.
|
||||
_target = _repeats ? [NSDate dateWithTimeIntervalSinceNow:_interval] : nil;
|
||||
if (_target && [_target timeIntervalSinceDate:now] <= 0) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)reschedule
|
||||
{
|
||||
// The JS Timers will do fine grained calculating of expired timeouts.
|
||||
_target = [NSDate dateWithTimeIntervalSinceNow:_interval];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface _RCTTimingProxy : NSObject
|
||||
|
@ -178,13 +182,11 @@ RCT_EXPORT_MODULE()
|
|||
- (void)didUpdateFrame:(RCTFrameUpdate *)update
|
||||
{
|
||||
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) {
|
||||
if ([timer updateFoundNeedsJSUpdate]) {
|
||||
[timersToCall addObject:timer.callbackID];
|
||||
}
|
||||
if (!timer.target) {
|
||||
[_timers removeObjectForKey:timer.callbackID];
|
||||
if ([timer shouldFire:now]) {
|
||||
[timersToCall addObject:timer];
|
||||
} else {
|
||||
nextScheduledTarget = [nextScheduledTarget earlierDate:timer.target];
|
||||
}
|
||||
|
@ -192,12 +194,24 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
// Call timers that need to be called
|
||||
if (timersToCall.count > 0) {
|
||||
NSArray<NSNumber *> *sortedTimers = [[timersToCall sortedArrayUsingComparator:^(_RCTTimer *a, _RCTTimer *b) {
|
||||
return [a.target compare:b.target];
|
||||
}] valueForKey:@"callbackID"];
|
||||
[_bridge enqueueJSCall:@"JSTimersExecution"
|
||||
method:@"callTimers"
|
||||
args:@[timersToCall]
|
||||
args:@[sortedTimers]
|
||||
completion:NULL];
|
||||
}
|
||||
|
||||
for (_RCTTimer *timer in timersToCall) {
|
||||
if (timer.repeats) {
|
||||
[timer reschedule];
|
||||
nextScheduledTarget = [nextScheduledTarget earlierDate:timer.target];
|
||||
} else {
|
||||
[_timers removeObjectForKey:timer.callbackID];
|
||||
}
|
||||
}
|
||||
|
||||
if (_sendIdleEvents) {
|
||||
NSTimeInterval frameElapsed = (CACurrentMediaTime() - update.timestamp);
|
||||
if (kFrameDuration - frameElapsed >= kIdleCallbackFrameDeadline) {
|
||||
|
|
Loading…
Reference in New Issue