mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-14 18:12:09 +08:00
feat(core): queueMacroTask (#8904)
This commit is contained in:
@ -1,12 +1,18 @@
|
|||||||
|
import { queueMacrotask } from '../utils/macrotask-scheduler';
|
||||||
import { FPSCallback } from '../fps-meter/fps-native';
|
import { FPSCallback } from '../fps-meter/fps-native';
|
||||||
import { getTimeInFrameBase } from './animation-native';
|
import { getTimeInFrameBase } from './animation-native';
|
||||||
|
import { Trace } from 'trace';
|
||||||
|
|
||||||
export interface FrameRequestCallback {
|
export interface FrameRequestCallback {
|
||||||
(time: number): void;
|
(time: number): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AnimationFrameCallbacks = { [key: string]: FrameRequestCallback };
|
||||||
|
|
||||||
let animationId = 0;
|
let animationId = 0;
|
||||||
let nextFrameAnimationCallbacks: { [key: string]: FrameRequestCallback } = {};
|
let currentFrameAnimationCallbacks: AnimationFrameCallbacks = {}; // requests that were scheduled in this frame and must be called ASAP
|
||||||
|
let currentFrameScheduled = false;
|
||||||
|
let nextFrameAnimationCallbacks: AnimationFrameCallbacks = {}; // requests there were scheduled in another request and must be called in the next frame
|
||||||
let shouldStop = true;
|
let shouldStop = true;
|
||||||
let inAnimationFrame = false;
|
let inAnimationFrame = false;
|
||||||
let fpsCallback: FPSCallback;
|
let fpsCallback: FPSCallback;
|
||||||
@ -23,33 +29,59 @@ function ensureNative() {
|
|||||||
fpsCallback = new FPSCallback(doFrame);
|
fpsCallback = new FPSCallback(doFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function callAnimationCallbacks(thisFrameCbs: AnimationFrameCallbacks, frameTime: number): void {
|
||||||
|
inAnimationFrame = true;
|
||||||
|
for (const animationId in thisFrameCbs) {
|
||||||
|
if (thisFrameCbs[animationId]) {
|
||||||
|
try {
|
||||||
|
thisFrameCbs[animationId](frameTime);
|
||||||
|
} catch (err) {
|
||||||
|
const msg = err ? err.stack || err : err;
|
||||||
|
Trace.write(`Error in requestAnimationFrame: ${msg}`, Trace.categories.Error, Trace.messageType.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inAnimationFrame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function doCurrentFrame() {
|
||||||
|
// if we're not getting accurate frame times
|
||||||
|
// set last frame time as the current time
|
||||||
|
if (!fpsCallback || !fpsCallback.running) {
|
||||||
|
lastFrameTime = getTimeInFrameBase();
|
||||||
|
}
|
||||||
|
currentFrameScheduled = false;
|
||||||
|
const thisFrameCbs = currentFrameAnimationCallbacks;
|
||||||
|
currentFrameAnimationCallbacks = {};
|
||||||
|
callAnimationCallbacks(thisFrameCbs, lastFrameTime);
|
||||||
|
}
|
||||||
|
|
||||||
function doFrame(currentTimeMillis: number) {
|
function doFrame(currentTimeMillis: number) {
|
||||||
lastFrameTime = currentTimeMillis;
|
lastFrameTime = currentTimeMillis;
|
||||||
shouldStop = true;
|
shouldStop = true;
|
||||||
const thisFrameCbs = nextFrameAnimationCallbacks;
|
const thisFrameCbs = nextFrameAnimationCallbacks;
|
||||||
nextFrameAnimationCallbacks = {};
|
nextFrameAnimationCallbacks = {};
|
||||||
inAnimationFrame = true;
|
callAnimationCallbacks(thisFrameCbs, lastFrameTime);
|
||||||
for (const animationId in thisFrameCbs) {
|
|
||||||
if (thisFrameCbs[animationId]) {
|
|
||||||
thisFrameCbs[animationId](lastFrameTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inAnimationFrame = false;
|
|
||||||
if (shouldStop) {
|
if (shouldStop) {
|
||||||
fpsCallback.stop(); // TODO: check performance without stopping to allow consistent frame times
|
fpsCallback.stop(); // TODO: check performance without stopping to allow consistent frame times
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function requestAnimationFrame(cb: FrameRequestCallback): number {
|
function ensureCurrentFrameScheduled() {
|
||||||
if (!inAnimationFrame) {
|
if (!currentFrameScheduled) {
|
||||||
inAnimationFrame = true;
|
currentFrameScheduled = true;
|
||||||
zonedCallback(cb)(getTimeInFrameBase()); // TODO: store and use lastFrameTime
|
queueMacrotask(doCurrentFrame);
|
||||||
inAnimationFrame = false;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return getNewId();
|
export function requestAnimationFrame(cb: FrameRequestCallback): number {
|
||||||
|
const animId = getNewId();
|
||||||
|
if (!inAnimationFrame) {
|
||||||
|
ensureCurrentFrameScheduled();
|
||||||
|
currentFrameAnimationCallbacks[animId] = zonedCallback(cb) as FrameRequestCallback;
|
||||||
|
return animId;
|
||||||
}
|
}
|
||||||
ensureNative();
|
ensureNative();
|
||||||
const animId = getNewId();
|
|
||||||
nextFrameAnimationCallbacks[animId] = zonedCallback(cb) as FrameRequestCallback;
|
nextFrameAnimationCallbacks[animId] = zonedCallback(cb) as FrameRequestCallback;
|
||||||
shouldStop = false;
|
shouldStop = false;
|
||||||
fpsCallback.start();
|
fpsCallback.start();
|
||||||
@ -57,6 +89,7 @@ export function requestAnimationFrame(cb: FrameRequestCallback): number {
|
|||||||
return animId;
|
return animId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cancelAnimationFrame(id: number) {
|
export function cancelAnimationFrame(id: number): void {
|
||||||
|
delete currentFrameAnimationCallbacks[id];
|
||||||
delete nextFrameAnimationCallbacks[id];
|
delete nextFrameAnimationCallbacks[id];
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ export * from './trace';
|
|||||||
|
|
||||||
export * from './ui';
|
export * from './ui';
|
||||||
|
|
||||||
import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, RESOURCE_PREFIX, FILE_PREFIX } from './utils';
|
import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, queueMacrotask, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, RESOURCE_PREFIX, FILE_PREFIX } from './utils';
|
||||||
import { ClassInfo, getClass, getBaseClasses, getClassInfo, isBoolean, isDefined, isFunction, isNullOrUndefined, isNumber, isObject, isString, isUndefined, toUIString, verifyCallback } from './utils/types';
|
import { ClassInfo, getClass, getBaseClasses, getClassInfo, isBoolean, isDefined, isFunction, isNullOrUndefined, isNumber, isObject, isString, isUndefined, toUIString, verifyCallback } from './utils/types';
|
||||||
|
|
||||||
export const Utils = {
|
export const Utils = {
|
||||||
@ -124,6 +124,7 @@ export const Utils = {
|
|||||||
mainThreadify,
|
mainThreadify,
|
||||||
isMainThread,
|
isMainThread,
|
||||||
dispatchToMainThread,
|
dispatchToMainThread,
|
||||||
|
queueMacrotask,
|
||||||
releaseNativeObject,
|
releaseNativeObject,
|
||||||
|
|
||||||
getModuleName,
|
getModuleName,
|
||||||
|
7
packages/core/utils/index.d.ts
vendored
7
packages/core/utils/index.d.ts
vendored
@ -1,6 +1,7 @@
|
|||||||
import { dip, px } from '../ui/core/view';
|
import { dip, px } from '../ui/core/view';
|
||||||
|
|
||||||
export * from './mainthread-helper';
|
export * from './mainthread-helper';
|
||||||
|
export * from './macrotask-scheduler';
|
||||||
export { Source } from './debug';
|
export { Source } from './debug';
|
||||||
|
|
||||||
export * from './native-helper';
|
export * from './native-helper';
|
||||||
@ -192,6 +193,12 @@ export function GC();
|
|||||||
*/
|
*/
|
||||||
export function releaseNativeObject(object: any /*java.lang.Object | NSObject*/);
|
export function releaseNativeObject(object: any /*java.lang.Object | NSObject*/);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues the passed function to be ran in a macroTask
|
||||||
|
* @param task the function to execute as a macroTask
|
||||||
|
*/
|
||||||
|
export function queueMacrotask(task: () => void): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the current thread is the main thread. Directly calls the passed function
|
* Checks if the current thread is the main thread. Directly calls the passed function
|
||||||
* if it is, or dispatches it to the main thread otherwise.
|
* if it is, or dispatches it to the main thread otherwise.
|
||||||
|
5
packages/core/utils/macrotask-scheduler.d.ts
vendored
Normal file
5
packages/core/utils/macrotask-scheduler.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/**
|
||||||
|
* Queues the passed function to be ran in a macroTask
|
||||||
|
* @param task the function to execute as a macroTask
|
||||||
|
*/
|
||||||
|
export function queueMacrotask(task: () => void): void;
|
27
packages/core/utils/macrotask-scheduler.ts
Normal file
27
packages/core/utils/macrotask-scheduler.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { Trace } from 'trace';
|
||||||
|
import { dispatchToMainThread } from './mainthread-helper';
|
||||||
|
|
||||||
|
let scheduled = false;
|
||||||
|
let macroTaskQueue: Array<() => void> = [];
|
||||||
|
|
||||||
|
function drainMacrotaskQueue() {
|
||||||
|
const currentQueue = macroTaskQueue;
|
||||||
|
macroTaskQueue = [];
|
||||||
|
scheduled = false;
|
||||||
|
currentQueue.forEach((task) => {
|
||||||
|
try {
|
||||||
|
task();
|
||||||
|
} catch (err) {
|
||||||
|
const msg = err ? err.stack || err : err;
|
||||||
|
Trace.write(`Error in macroTask: ${msg}`, Trace.categories.Error, Trace.messageType.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function queueMacrotask(task: () => void): void {
|
||||||
|
macroTaskQueue.push(task);
|
||||||
|
if (!scheduled) {
|
||||||
|
scheduled = true;
|
||||||
|
dispatchToMainThread(drainMacrotaskQueue);
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ import * as layout from './layout-helper';
|
|||||||
|
|
||||||
export { layout };
|
export { layout };
|
||||||
export * from './mainthread-helper';
|
export * from './mainthread-helper';
|
||||||
|
export * from './macrotask-scheduler';
|
||||||
|
|
||||||
export const RESOURCE_PREFIX = 'res://';
|
export const RESOURCE_PREFIX = 'res://';
|
||||||
export const FILE_PREFIX = 'file:///';
|
export const FILE_PREFIX = 'file:///';
|
||||||
|
Reference in New Issue
Block a user