mirror of
https://github.com/status-im/react-native.git
synced 2025-02-27 08:30:34 +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.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*
|
*
|
||||||
* @providesModule JSTimers
|
* @providesModule JSTimers
|
||||||
|
* @flow
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Note that the module JSTimers is split into two in order to solve a cycle
|
// Note that the module JSTimers is split into two in order to solve a cycle
|
||||||
// in dependencies. NativeModules > BatchedBridge > MessageQueue > JSTimersExecution
|
// in dependencies. NativeModules > BatchedBridge > MessageQueue > JSTimersExecution
|
||||||
var RCTTiming = require('NativeModules').Timing;
|
const RCTTiming = require('NativeModules').Timing;
|
||||||
var JSTimersExecution = require('JSTimersExecution');
|
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
|
* JS implementation of timer functions. Must be completely driven by an
|
||||||
* external clock signal, all that's stored here is timerID, timer type, and
|
* external clock signal, all that's stored here is timerID, timer type, and
|
||||||
* callback.
|
* callback.
|
||||||
*/
|
*/
|
||||||
var JSTimers = {
|
const 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;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function} func Callback to be invoked after `duration` ms.
|
* @param {function} func Callback to be invoked after `duration` ms.
|
||||||
* @param {number} duration Number of milliseconds.
|
* @param {number} duration Number of milliseconds.
|
||||||
*/
|
*/
|
||||||
setTimeout: function(func, duration, ...args) {
|
setTimeout: function(func: Function, duration: number, ...args?: any): number {
|
||||||
var newID = JSTimersExecution.GUID++;
|
const id = _allocateCallback(() => func.apply(undefined, args),
|
||||||
var freeIndex = JSTimers._getFreeIndex();
|
JSTimersExecution.Type.setTimeout);
|
||||||
JSTimersExecution.timerIDs[freeIndex] = newID;
|
RCTTiming.createTimer(id, duration || 0, Date.now(), /* recurring */ false);
|
||||||
JSTimersExecution.callbacks[freeIndex] = function() {
|
return id;
|
||||||
return func.apply(undefined, args);
|
|
||||||
};
|
|
||||||
JSTimersExecution.types[freeIndex] = JSTimersExecution.Type.setTimeout;
|
|
||||||
RCTTiming.createTimer(newID, duration || 0, Date.now(), /** recurring */ false);
|
|
||||||
return newID;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function} func Callback to be invoked every `duration` ms.
|
* @param {function} func Callback to be invoked every `duration` ms.
|
||||||
* @param {number} duration Number of milliseconds.
|
* @param {number} duration Number of milliseconds.
|
||||||
*/
|
*/
|
||||||
setInterval: function(func, duration, ...args) {
|
setInterval: function(func: Function, duration: number, ...args?: any): number {
|
||||||
var newID = JSTimersExecution.GUID++;
|
const id = _allocateCallback(() => func.apply(undefined, args),
|
||||||
var freeIndex = JSTimers._getFreeIndex();
|
JSTimersExecution.Type.setInterval);
|
||||||
JSTimersExecution.timerIDs[freeIndex] = newID;
|
RCTTiming.createTimer(id, duration || 0, Date.now(), /* recurring */ true);
|
||||||
JSTimersExecution.callbacks[freeIndex] = function() {
|
return id;
|
||||||
return func.apply(undefined, args);
|
|
||||||
};
|
|
||||||
JSTimersExecution.types[freeIndex] = JSTimersExecution.Type.setInterval;
|
|
||||||
RCTTiming.createTimer(newID, duration || 0, Date.now(), /** recurring */ true);
|
|
||||||
return newID;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function} func Callback to be invoked before the end of the
|
* @param {function} func Callback to be invoked before the end of the
|
||||||
* current JavaScript execution loop.
|
* current JavaScript execution loop.
|
||||||
*/
|
*/
|
||||||
setImmediate: function(func, ...args) {
|
setImmediate: function(func: Function, ...args?: any) {
|
||||||
var newID = JSTimersExecution.GUID++;
|
const id = _allocateCallback(() => func.apply(undefined, args),
|
||||||
var freeIndex = JSTimers._getFreeIndex();
|
JSTimersExecution.Type.setImmediate);
|
||||||
JSTimersExecution.timerIDs[freeIndex] = newID;
|
JSTimersExecution.immediates.push(id);
|
||||||
JSTimersExecution.callbacks[freeIndex] = function() {
|
return id;
|
||||||
return func.apply(undefined, args);
|
|
||||||
};
|
|
||||||
JSTimersExecution.types[freeIndex] = JSTimersExecution.Type.setImmediate;
|
|
||||||
JSTimersExecution.immediates.push(newID);
|
|
||||||
return newID;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function} func Callback to be invoked every frame.
|
* @param {function} func Callback to be invoked every frame.
|
||||||
*/
|
*/
|
||||||
requestAnimationFrame: function(func) {
|
requestAnimationFrame: function(func : Function) {
|
||||||
var newID = JSTimersExecution.GUID++;
|
const id = _allocateCallback(func, JSTimersExecution.Type.requestAnimationFrame);
|
||||||
var freeIndex = JSTimers._getFreeIndex();
|
RCTTiming.createTimer(id, 1, Date.now(), /* recurring */ false);
|
||||||
JSTimersExecution.timerIDs[freeIndex] = newID;
|
return id;
|
||||||
JSTimersExecution.callbacks[freeIndex] = func;
|
|
||||||
JSTimersExecution.types[freeIndex] = JSTimersExecution.Type.requestAnimationFrame;
|
|
||||||
RCTTiming.createTimer(newID, 1, Date.now(), /** recurring */ false);
|
|
||||||
return newID;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function} func Callback to be invoked every frame and provided
|
* @param {function} func Callback to be invoked every frame and provided
|
||||||
* with time remaining in frame.
|
* with time remaining in frame.
|
||||||
*/
|
*/
|
||||||
requestIdleCallback: function(func) {
|
requestIdleCallback: function(func : Function) {
|
||||||
if (JSTimersExecution.requestIdleCallbacks.length === 0) {
|
if (JSTimersExecution.requestIdleCallbacks.length === 0) {
|
||||||
RCTTiming.setSendIdleEvents(true);
|
RCTTiming.setSendIdleEvents(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var newID = JSTimersExecution.GUID++;
|
const id = _allocateCallback(func, JSTimersExecution.Type.requestIdleCallback);
|
||||||
var freeIndex = JSTimers._getFreeIndex();
|
JSTimersExecution.requestIdleCallbacks.push(id);
|
||||||
JSTimersExecution.timerIDs[freeIndex] = newID;
|
return id;
|
||||||
JSTimersExecution.callbacks[freeIndex] = func;
|
|
||||||
JSTimersExecution.types[freeIndex] = JSTimersExecution.Type.requestIdleCallback;
|
|
||||||
JSTimersExecution.requestIdleCallbacks.push(newID);
|
|
||||||
return newID;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelIdleCallback: function(timerID) {
|
cancelIdleCallback: function(timerID: number) {
|
||||||
JSTimers._clearTimerID(timerID);
|
_freeCallback(timerID);
|
||||||
var index = JSTimersExecution.requestIdleCallbacks.indexOf(timerID);
|
const index = JSTimersExecution.requestIdleCallbacks.indexOf(timerID);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
JSTimersExecution.requestIdleCallbacks.splice(index, 1);
|
JSTimersExecution.requestIdleCallbacks.splice(index, 1);
|
||||||
}
|
}
|
||||||
@ -126,43 +127,24 @@ var JSTimers = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
clearTimeout: function(timerID) {
|
clearTimeout: function(timerID: number) {
|
||||||
JSTimers._clearTimerID(timerID);
|
_freeCallback(timerID);
|
||||||
},
|
},
|
||||||
|
|
||||||
clearInterval: function(timerID) {
|
clearInterval: function(timerID: number) {
|
||||||
JSTimers._clearTimerID(timerID);
|
_freeCallback(timerID);
|
||||||
},
|
},
|
||||||
|
|
||||||
clearImmediate: function(timerID) {
|
clearImmediate: function(timerID: number) {
|
||||||
JSTimers._clearTimerID(timerID);
|
_freeCallback(timerID);
|
||||||
var index = JSTimersExecution.immediates.indexOf(timerID);
|
const index = JSTimersExecution.immediates.indexOf(timerID);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
JSTimersExecution.immediates.splice(index, 1);
|
JSTimersExecution.immediates.splice(index, 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelAnimationFrame: function(timerID) {
|
cancelAnimationFrame: function(timerID: number) {
|
||||||
JSTimers._clearTimerID(timerID);
|
_freeCallback(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*
|
*
|
||||||
* @providesModule JSTimersExecution
|
* @providesModule JSTimersExecution
|
||||||
|
* @flow
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
@ -39,35 +40,43 @@ const JSTimersExecution = {
|
|||||||
requestIdleCallback: null,
|
requestIdleCallback: null,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// Parallel arrays:
|
// Parallel arrays
|
||||||
callbacks: [],
|
callbacks: [],
|
||||||
types: [],
|
types: [],
|
||||||
timerIDs: [],
|
timerIDs: [],
|
||||||
immediates: [],
|
immediates: [],
|
||||||
requestIdleCallbacks: [],
|
requestIdleCallbacks: [],
|
||||||
|
|
||||||
|
errors: (null : ?[Error]),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls the callback associated with the ID. Also unregister that callback
|
* 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
|
* if it was a one time timer (setTimeout), and not unregister it if it was
|
||||||
* recurring (setInterval).
|
* recurring (setInterval).
|
||||||
*/
|
*/
|
||||||
callTimer(timerID, frameTime) {
|
callTimer(timerID: number, frameTime: number) {
|
||||||
warning(
|
warning(
|
||||||
timerID <= JSTimersExecution.GUID,
|
timerID <= JSTimersExecution.GUID,
|
||||||
'Tried to call timer with ID %s but no such timer exists.',
|
'Tried to call timer with ID %s but no such timer exists.',
|
||||||
timerID
|
timerID
|
||||||
);
|
);
|
||||||
const timerIndex = JSTimersExecution.timerIDs.indexOf(timerID);
|
|
||||||
// timerIndex of -1 means that no timer with that ID exists. There are
|
// 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
|
// two situations when this happens, when a garbage timer ID was given
|
||||||
// and when a previously existing timer was deleted before this callback
|
// 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
|
// fired. In both cases we want to ignore the timer id, but in the former
|
||||||
// case we warn as well.
|
// case we warn as well.
|
||||||
|
const timerIndex = JSTimersExecution.timerIDs.indexOf(timerID);
|
||||||
if (timerIndex === -1) {
|
if (timerIndex === -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = JSTimersExecution.types[timerIndex];
|
const type = JSTimersExecution.types[timerIndex];
|
||||||
const callback = JSTimersExecution.callbacks[timerIndex];
|
const callback = JSTimersExecution.callbacks[timerIndex];
|
||||||
|
if (!callback || !type) {
|
||||||
|
console.error('No callback found for timerID ' + timerID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Clear the metadata
|
// Clear the metadata
|
||||||
if (type === JSTimersExecution.Type.setTimeout ||
|
if (type === JSTimersExecution.Type.setTimeout ||
|
||||||
@ -83,8 +92,7 @@ const JSTimersExecution = {
|
|||||||
type === JSTimersExecution.Type.setImmediate) {
|
type === JSTimersExecution.Type.setImmediate) {
|
||||||
callback();
|
callback();
|
||||||
} else if (type === JSTimersExecution.Type.requestAnimationFrame) {
|
} else if (type === JSTimersExecution.Type.requestAnimationFrame) {
|
||||||
const currentTime = performanceNow();
|
callback(performanceNow());
|
||||||
callback(currentTime);
|
|
||||||
} else if (type === JSTimersExecution.Type.requestIdleCallback) {
|
} else if (type === JSTimersExecution.Type.requestIdleCallback) {
|
||||||
callback({
|
callback({
|
||||||
timeRemaining: function() {
|
timeRemaining: function() {
|
||||||
@ -96,12 +104,14 @@ const JSTimersExecution = {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error('Tried to call a callback with invalid type: ' + type);
|
console.error('Tried to call a callback with invalid type: ' + type);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Don't rethrow so that we can run every other timer.
|
// Don't rethrow so that we can run all timers.
|
||||||
JSTimersExecution.errors = JSTimersExecution.errors || [];
|
if (!JSTimersExecution.errors) {
|
||||||
JSTimersExecution.errors.push(e);
|
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,
|
* This is called from the native side. We are passed an array of timerIDs,
|
||||||
* and
|
* and
|
||||||
*/
|
*/
|
||||||
callTimers(timerIDs) {
|
callTimers(timerIDs: [number]) {
|
||||||
invariant(
|
invariant(
|
||||||
timerIDs.length !== 0,
|
timerIDs.length !== 0,
|
||||||
'Cannot call `callTimers` with an empty list of IDs.'
|
'Cannot call `callTimers` with an empty list of IDs.'
|
||||||
);
|
);
|
||||||
|
|
||||||
JSTimersExecution.errors = null;
|
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;
|
const errors = JSTimersExecution.errors;
|
||||||
if (errors) {
|
if (errors) {
|
||||||
@ -135,15 +147,12 @@ const JSTimersExecution = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
callIdleCallbacks: function(frameTime) {
|
callIdleCallbacks: function(frameTime: number) {
|
||||||
const { Timing } = require('NativeModules');
|
|
||||||
|
|
||||||
if (FRAME_DURATION - (performanceNow() - frameTime) < IDLE_CALLBACK_FRAME_DEADLINE) {
|
if (FRAME_DURATION - (performanceNow() - frameTime) < IDLE_CALLBACK_FRAME_DEADLINE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSTimersExecution.errors = null;
|
JSTimersExecution.errors = null;
|
||||||
|
|
||||||
if (JSTimersExecution.requestIdleCallbacks.length > 0) {
|
if (JSTimersExecution.requestIdleCallbacks.length > 0) {
|
||||||
const passIdleCallbacks = JSTimersExecution.requestIdleCallbacks.slice();
|
const passIdleCallbacks = JSTimersExecution.requestIdleCallbacks.slice();
|
||||||
JSTimersExecution.requestIdleCallbacks = [];
|
JSTimersExecution.requestIdleCallbacks = [];
|
||||||
@ -154,6 +163,7 @@ const JSTimersExecution = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (JSTimersExecution.requestIdleCallbacks.length === 0) {
|
if (JSTimersExecution.requestIdleCallbacks.length === 0) {
|
||||||
|
const { Timing } = require('NativeModules');
|
||||||
Timing.setSendIdleEvents(false);
|
Timing.setSendIdleEvents(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +190,7 @@ const JSTimersExecution = {
|
|||||||
// Use for loop rather than forEach as per @vjeux's advice
|
// Use for loop rather than forEach as per @vjeux's advice
|
||||||
// https://github.com/facebook/react-native/commit/c8fd9f7588ad02d2293cac7224715f4af7b0f352#commitcomment-14570051
|
// https://github.com/facebook/react-native/commit/c8fd9f7588ad02d2293cac7224715f4af7b0f352#commitcomment-14570051
|
||||||
for (let i = 0; i < passImmediates.length; ++i) {
|
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.
|
* Called from native (in development) when environment times are out-of-sync.
|
||||||
*/
|
*/
|
||||||
emitTimeDriftWarning(warningMessage) {
|
emitTimeDriftWarning(warningMessage: string) {
|
||||||
if (hasEmittedTimeDriftWarning) {
|
if (hasEmittedTimeDriftWarning) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -214,7 +224,7 @@ const JSTimersExecution = {
|
|||||||
console.warn(warningMessage);
|
console.warn(warningMessage);
|
||||||
},
|
},
|
||||||
|
|
||||||
_clearIndex(i) {
|
_clearIndex(i: number) {
|
||||||
JSTimersExecution.timerIDs[i] = null;
|
JSTimersExecution.timerIDs[i] = null;
|
||||||
JSTimersExecution.callbacks[i] = null;
|
JSTimersExecution.callbacks[i] = null;
|
||||||
JSTimersExecution.types[i] = null;
|
JSTimersExecution.types[i] = null;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user