mirror of
https://github.com/status-im/react-native.git
synced 2025-02-11 17:07:09 +00:00
Summary: These are bad since the app might not be foregrounded anymore and it also keeps the timing module awake. We could fix the latter but the former would require someone adding AlarmManager support. Open to better/more helpful ideas for the warning message but there's not a ton we can tell them to do right now. Reviewed By: achen1 Differential Revision: D4716273 fbshipit-source-id: c5d3a3ce8af53253b240477f2bde38094a138a02
179 lines
5.8 KiB
JavaScript
179 lines
5.8 KiB
JavaScript
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* 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
|
|
const RCTTiming = require('NativeModules').Timing;
|
|
const JSTimersExecution = require('JSTimersExecution');
|
|
const Platform = require('Platform');
|
|
|
|
const parseErrorStack = require('parseErrorStack');
|
|
|
|
import type {JSTimerType} from '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: JSTimerType): number {
|
|
const id = JSTimersExecution.GUID++;
|
|
const freeIndex = _getFreeIndex();
|
|
JSTimersExecution.timerIDs[freeIndex] = id;
|
|
JSTimersExecution.callbacks[freeIndex] = func;
|
|
JSTimersExecution.types[freeIndex] = type;
|
|
if (__DEV__) {
|
|
const e = (new Error() : any);
|
|
e.framesToPop = 1;
|
|
const stack = parseErrorStack(e);
|
|
if (stack) {
|
|
JSTimersExecution.identifiers[freeIndex] = stack.shift();
|
|
}
|
|
}
|
|
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 !== 'setImmediate' && type !== 'requestIdleCallback') {
|
|
RCTTiming.deleteTimer(timerID);
|
|
}
|
|
}
|
|
}
|
|
|
|
const MAX_TIMER_DURATION_MS = 60 * 1000;
|
|
const IS_ANDROID = Platform.OS === 'android';
|
|
const ANDROID_LONG_TIMER_MESSAGE =
|
|
'Setting a timer for a long period of time, i.e. multiple minutes, is a ' +
|
|
'performance and correctness issue on Android as it keeps the timer ' +
|
|
'module awake, and timers can only be called when the app is in the foreground. ' +
|
|
'See https://github.com/facebook/react-native/issues/12981 for more info.';
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
const JSTimers = {
|
|
/**
|
|
* @param {function} func Callback to be invoked after `duration` ms.
|
|
* @param {number} duration Number of milliseconds.
|
|
*/
|
|
setTimeout: function(func: Function, duration: number, ...args?: any): number {
|
|
if (IS_ANDROID && duration > MAX_TIMER_DURATION_MS) {
|
|
console.warn(
|
|
ANDROID_LONG_TIMER_MESSAGE + '\n' + '(Saw setTimeout with duration ' +
|
|
duration + 'ms)');
|
|
}
|
|
const id = _allocateCallback(() => func.apply(undefined, args), '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: Function, duration: number, ...args?: any): number {
|
|
if (IS_ANDROID && duration > MAX_TIMER_DURATION_MS) {
|
|
console.warn(
|
|
ANDROID_LONG_TIMER_MESSAGE + '\n' + '(Saw setInterval with duration ' +
|
|
duration + 'ms)');
|
|
}
|
|
const id = _allocateCallback(() => func.apply(undefined, args), '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: Function, ...args?: any) {
|
|
const id = _allocateCallback(() => func.apply(undefined, args), 'setImmediate');
|
|
JSTimersExecution.immediates.push(id);
|
|
return id;
|
|
},
|
|
|
|
/**
|
|
* @param {function} func Callback to be invoked every frame.
|
|
*/
|
|
requestAnimationFrame: function(func : Function) {
|
|
const id = _allocateCallback(func, '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 : Function) {
|
|
if (JSTimersExecution.requestIdleCallbacks.length === 0) {
|
|
RCTTiming.setSendIdleEvents(true);
|
|
}
|
|
|
|
const id = _allocateCallback(func, 'requestIdleCallback');
|
|
JSTimersExecution.requestIdleCallbacks.push(id);
|
|
return id;
|
|
},
|
|
|
|
cancelIdleCallback: function(timerID: number) {
|
|
_freeCallback(timerID);
|
|
const index = JSTimersExecution.requestIdleCallbacks.indexOf(timerID);
|
|
if (index !== -1) {
|
|
JSTimersExecution.requestIdleCallbacks.splice(index, 1);
|
|
}
|
|
|
|
if (JSTimersExecution.requestIdleCallbacks.length === 0) {
|
|
RCTTiming.setSendIdleEvents(false);
|
|
}
|
|
},
|
|
|
|
clearTimeout: function(timerID: number) {
|
|
_freeCallback(timerID);
|
|
},
|
|
|
|
clearInterval: function(timerID: number) {
|
|
_freeCallback(timerID);
|
|
},
|
|
|
|
clearImmediate: function(timerID: number) {
|
|
_freeCallback(timerID);
|
|
const index = JSTimersExecution.immediates.indexOf(timerID);
|
|
if (index !== -1) {
|
|
JSTimersExecution.immediates.splice(index, 1);
|
|
}
|
|
},
|
|
|
|
cancelAnimationFrame: function(timerID: number) {
|
|
_freeCallback(timerID);
|
|
},
|
|
};
|
|
|
|
module.exports = JSTimers;
|