Flow-ify and cleanup JSTimers

Reviewed By: davidaurelio

Differential Revision: D3819511

fbshipit-source-id: 849133d013b026c3f95e988664f252bc35c65122
This commit is contained in:
Pieter De Baets 2016-09-06 03:57:58 -07:00 committed by Facebook Github Bot 4
parent f07ca31303
commit 66ce1297c2
2 changed files with 104 additions and 112 deletions

View File

@ -7,116 +7,117 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule JSTimers
* @flow
*/
'use strict';
// Note that the module JSTimers is split into two in order to solve a cycle
// in dependencies. NativeModules > BatchedBridge > MessageQueue > JSTimersExecution
var RCTTiming = require('NativeModules').Timing;
var JSTimersExecution = require('JSTimersExecution');
const RCTTiming = require('NativeModules').Timing;
const JSTimersExecution = require('JSTimersExecution');
// Returns a free index if one is available, and the next consecutive index otherwise.
function _getFreeIndex(): number {
let freeIndex = JSTimersExecution.timerIDs.indexOf(null);
if (freeIndex === -1) {
freeIndex = JSTimersExecution.timerIDs.length;
}
return freeIndex;
}
function _allocateCallback(func: Function, type: $Keys<typeof JSTimersExecution.Type>): number {
const id = JSTimersExecution.GUID++;
const freeIndex = _getFreeIndex();
JSTimersExecution.timerIDs[freeIndex] = id;
JSTimersExecution.callbacks[freeIndex] = func;
JSTimersExecution.types[freeIndex] = type;
return id;
}
function _freeCallback(timerID: number) {
// JSTimersExecution.timerIDs contains nulls after timers have been removed;
// ignore nulls upfront so indexOf doesn't find them
if (timerID == null) {
return;
}
const index = JSTimersExecution.timerIDs.indexOf(timerID);
// See corresponding comment in `callTimers` for reasoning behind this
if (index !== -1) {
JSTimersExecution._clearIndex(index);
const type = JSTimersExecution.types[index];
if (type !== JSTimersExecution.Type.setImmediate &&
type !== JSTimersExecution.Type.requestIdleCallback) {
RCTTiming.deleteTimer(timerID);
}
}
}
/**
* JS implementation of timer functions. Must be completely driven by an
* external clock signal, all that's stored here is timerID, timer type, and
* callback.
*/
var JSTimers = {
Types: JSTimersExecution.Types,
/**
* Returns a free index if one is available, and the next consecutive index
* otherwise.
*/
_getFreeIndex: function() {
var freeIndex = JSTimersExecution.timerIDs.indexOf(null);
if (freeIndex === -1) {
freeIndex = JSTimersExecution.timerIDs.length;
}
return freeIndex;
},
const JSTimers = {
/**
* @param {function} func Callback to be invoked after `duration` ms.
* @param {number} duration Number of milliseconds.
*/
setTimeout: function(func, duration, ...args) {
var newID = JSTimersExecution.GUID++;
var freeIndex = JSTimers._getFreeIndex();
JSTimersExecution.timerIDs[freeIndex] = newID;
JSTimersExecution.callbacks[freeIndex] = function() {
return func.apply(undefined, args);
};
JSTimersExecution.types[freeIndex] = JSTimersExecution.Type.setTimeout;
RCTTiming.createTimer(newID, duration || 0, Date.now(), /** recurring */ false);
return newID;
setTimeout: function(func: Function, duration: number, ...args?: any): number {
const id = _allocateCallback(() => func.apply(undefined, args),
JSTimersExecution.Type.setTimeout);
RCTTiming.createTimer(id, duration || 0, Date.now(), /* recurring */ false);
return id;
},
/**
* @param {function} func Callback to be invoked every `duration` ms.
* @param {number} duration Number of milliseconds.
*/
setInterval: function(func, duration, ...args) {
var newID = JSTimersExecution.GUID++;
var freeIndex = JSTimers._getFreeIndex();
JSTimersExecution.timerIDs[freeIndex] = newID;
JSTimersExecution.callbacks[freeIndex] = function() {
return func.apply(undefined, args);
};
JSTimersExecution.types[freeIndex] = JSTimersExecution.Type.setInterval;
RCTTiming.createTimer(newID, duration || 0, Date.now(), /** recurring */ true);
return newID;
setInterval: function(func: Function, duration: number, ...args?: any): number {
const id = _allocateCallback(() => func.apply(undefined, args),
JSTimersExecution.Type.setInterval);
RCTTiming.createTimer(id, duration || 0, Date.now(), /* recurring */ true);
return id;
},
/**
* @param {function} func Callback to be invoked before the end of the
* current JavaScript execution loop.
*/
setImmediate: function(func, ...args) {
var newID = JSTimersExecution.GUID++;
var freeIndex = JSTimers._getFreeIndex();
JSTimersExecution.timerIDs[freeIndex] = newID;
JSTimersExecution.callbacks[freeIndex] = function() {
return func.apply(undefined, args);
};
JSTimersExecution.types[freeIndex] = JSTimersExecution.Type.setImmediate;
JSTimersExecution.immediates.push(newID);
return newID;
setImmediate: function(func: Function, ...args?: any) {
const id = _allocateCallback(() => func.apply(undefined, args),
JSTimersExecution.Type.setImmediate);
JSTimersExecution.immediates.push(id);
return id;
},
/**
* @param {function} func Callback to be invoked every frame.
*/
requestAnimationFrame: function(func) {
var newID = JSTimersExecution.GUID++;
var freeIndex = JSTimers._getFreeIndex();
JSTimersExecution.timerIDs[freeIndex] = newID;
JSTimersExecution.callbacks[freeIndex] = func;
JSTimersExecution.types[freeIndex] = JSTimersExecution.Type.requestAnimationFrame;
RCTTiming.createTimer(newID, 1, Date.now(), /** recurring */ false);
return newID;
requestAnimationFrame: function(func : Function) {
const id = _allocateCallback(func, JSTimersExecution.Type.requestAnimationFrame);
RCTTiming.createTimer(id, 1, Date.now(), /* recurring */ false);
return id;
},
/**
* @param {function} func Callback to be invoked every frame and provided
* with time remaining in frame.
*/
requestIdleCallback: function(func) {
requestIdleCallback: function(func : Function) {
if (JSTimersExecution.requestIdleCallbacks.length === 0) {
RCTTiming.setSendIdleEvents(true);
}
var newID = JSTimersExecution.GUID++;
var freeIndex = JSTimers._getFreeIndex();
JSTimersExecution.timerIDs[freeIndex] = newID;
JSTimersExecution.callbacks[freeIndex] = func;
JSTimersExecution.types[freeIndex] = JSTimersExecution.Type.requestIdleCallback;
JSTimersExecution.requestIdleCallbacks.push(newID);
return newID;
const id = _allocateCallback(func, JSTimersExecution.Type.requestIdleCallback);
JSTimersExecution.requestIdleCallbacks.push(id);
return id;
},
cancelIdleCallback: function(timerID) {
JSTimers._clearTimerID(timerID);
var index = JSTimersExecution.requestIdleCallbacks.indexOf(timerID);
cancelIdleCallback: function(timerID: number) {
_freeCallback(timerID);
const index = JSTimersExecution.requestIdleCallbacks.indexOf(timerID);
if (index !== -1) {
JSTimersExecution.requestIdleCallbacks.splice(index, 1);
}
@ -126,43 +127,24 @@ var JSTimers = {
}
},
clearTimeout: function(timerID) {
JSTimers._clearTimerID(timerID);
clearTimeout: function(timerID: number) {
_freeCallback(timerID);
},
clearInterval: function(timerID) {
JSTimers._clearTimerID(timerID);
clearInterval: function(timerID: number) {
_freeCallback(timerID);
},
clearImmediate: function(timerID) {
JSTimers._clearTimerID(timerID);
var index = JSTimersExecution.immediates.indexOf(timerID);
clearImmediate: function(timerID: number) {
_freeCallback(timerID);
const index = JSTimersExecution.immediates.indexOf(timerID);
if (index !== -1) {
JSTimersExecution.immediates.splice(index, 1);
}
},
cancelAnimationFrame: function(timerID) {
JSTimers._clearTimerID(timerID);
},
_clearTimerID: function(timerID) {
// JSTimersExecution.timerIDs contains nulls after timers have been removed;
// ignore nulls upfront so indexOf doesn't find them
if (timerID == null) {
return;
}
var index = JSTimersExecution.timerIDs.indexOf(timerID);
// See corresponding comment in `callTimers` for reasoning behind this
if (index !== -1) {
JSTimersExecution._clearIndex(index);
var type = JSTimersExecution.types[index];
if (type !== JSTimersExecution.Type.setImmediate &&
type !== JSTimersExecution.Type.requestIdleCallback) {
RCTTiming.deleteTimer(timerID);
}
}
cancelAnimationFrame: function(timerID: number) {
_freeCallback(timerID);
},
};

View File

@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule JSTimersExecution
* @flow
*/
'use strict';
@ -39,35 +40,43 @@ const JSTimersExecution = {
requestIdleCallback: null,
}),
// Parallel arrays:
// Parallel arrays
callbacks: [],
types: [],
timerIDs: [],
immediates: [],
requestIdleCallbacks: [],
errors: (null : ?[Error]),
/**
* Calls the callback associated with the ID. Also unregister that callback
* if it was a one time timer (setTimeout), and not unregister it if it was
* recurring (setInterval).
*/
callTimer(timerID, frameTime) {
callTimer(timerID: number, frameTime: number) {
warning(
timerID <= JSTimersExecution.GUID,
'Tried to call timer with ID %s but no such timer exists.',
timerID
);
const timerIndex = JSTimersExecution.timerIDs.indexOf(timerID);
// timerIndex of -1 means that no timer with that ID exists. There are
// two situations when this happens, when a garbage timer ID was given
// and when a previously existing timer was deleted before this callback
// fired. In both cases we want to ignore the timer id, but in the former
// case we warn as well.
const timerIndex = JSTimersExecution.timerIDs.indexOf(timerID);
if (timerIndex === -1) {
return;
}
const type = JSTimersExecution.types[timerIndex];
const callback = JSTimersExecution.callbacks[timerIndex];
if (!callback || !type) {
console.error('No callback found for timerID ' + timerID);
return;
}
// Clear the metadata
if (type === JSTimersExecution.Type.setTimeout ||
@ -83,8 +92,7 @@ const JSTimersExecution = {
type === JSTimersExecution.Type.setImmediate) {
callback();
} else if (type === JSTimersExecution.Type.requestAnimationFrame) {
const currentTime = performanceNow();
callback(currentTime);
callback(performanceNow());
} else if (type === JSTimersExecution.Type.requestIdleCallback) {
callback({
timeRemaining: function() {
@ -96,12 +104,14 @@ const JSTimersExecution = {
});
} else {
console.error('Tried to call a callback with invalid type: ' + type);
return;
}
} catch (e) {
// Don't rethrow so that we can run every other timer.
JSTimersExecution.errors = JSTimersExecution.errors || [];
JSTimersExecution.errors.push(e);
// Don't rethrow so that we can run all timers.
if (!JSTimersExecution.errors) {
JSTimersExecution.errors = [e];
} else {
JSTimersExecution.errors.push(e);
}
}
},
@ -109,14 +119,16 @@ const JSTimersExecution = {
* This is called from the native side. We are passed an array of timerIDs,
* and
*/
callTimers(timerIDs) {
callTimers(timerIDs: [number]) {
invariant(
timerIDs.length !== 0,
'Cannot call `callTimers` with an empty list of IDs.'
);
JSTimersExecution.errors = null;
timerIDs.forEach((id) => { JSTimersExecution.callTimer(id); });
for (let i = 0; i < timerIDs.length; i++) {
JSTimersExecution.callTimer(timerIDs[i], 0);
}
const errors = JSTimersExecution.errors;
if (errors) {
@ -135,15 +147,12 @@ const JSTimersExecution = {
}
},
callIdleCallbacks: function(frameTime) {
const { Timing } = require('NativeModules');
callIdleCallbacks: function(frameTime: number) {
if (FRAME_DURATION - (performanceNow() - frameTime) < IDLE_CALLBACK_FRAME_DEADLINE) {
return;
}
JSTimersExecution.errors = null;
if (JSTimersExecution.requestIdleCallbacks.length > 0) {
const passIdleCallbacks = JSTimersExecution.requestIdleCallbacks.slice();
JSTimersExecution.requestIdleCallbacks = [];
@ -154,6 +163,7 @@ const JSTimersExecution = {
}
if (JSTimersExecution.requestIdleCallbacks.length === 0) {
const { Timing } = require('NativeModules');
Timing.setSendIdleEvents(false);
}
@ -180,7 +190,7 @@ const JSTimersExecution = {
// Use for loop rather than forEach as per @vjeux's advice
// https://github.com/facebook/react-native/commit/c8fd9f7588ad02d2293cac7224715f4af7b0f352#commitcomment-14570051
for (let i = 0; i < passImmediates.length; ++i) {
JSTimersExecution.callTimer(passImmediates[i]);
JSTimersExecution.callTimer(passImmediates[i], 0);
}
}
@ -206,7 +216,7 @@ const JSTimersExecution = {
/**
* Called from native (in development) when environment times are out-of-sync.
*/
emitTimeDriftWarning(warningMessage) {
emitTimeDriftWarning(warningMessage: string) {
if (hasEmittedTimeDriftWarning) {
return;
}
@ -214,7 +224,7 @@ const JSTimersExecution = {
console.warn(warningMessage);
},
_clearIndex(i) {
_clearIndex(i: number) {
JSTimersExecution.timerIDs[i] = null;
JSTimersExecution.callbacks[i] = null;
JSTimersExecution.types[i] = null;