mirror of
https://github.com/status-im/react-native.git
synced 2025-02-27 00:20:31 +00:00
Flow-ify and cleanup JSTimers
Reviewed By: davidaurelio Differential Revision: D3819511 fbshipit-source-id: 849133d013b026c3f95e988664f252bc35c65122
This commit is contained in:
parent
f07ca31303
commit
66ce1297c2
@ -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);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user