frame-scheduling
Advanced tools
Comparing version 0.6.0 to 0.7.0
{ | ||
"name": "frame-scheduling", | ||
"version": "0.6.0", | ||
"version": "0.7.0", | ||
"description": "Asynchronous start of functions in JS. Supports priority and interrupt execution every 16 milliseconds, to achieve 60fps.", | ||
"main": "lib/frameScheduling.js", | ||
"scripts": { | ||
"build": "tsc", | ||
"prepublish": "npm run build", | ||
"build": "rollup -c", | ||
"prepublishOnly": "npm run build", | ||
"test": "jest", | ||
@@ -14,2 +13,7 @@ "test:coverage": "jest --coverage", | ||
}, | ||
"files": [ "dist", "src" ], | ||
"main": "dist/frameScheduling.js", | ||
"module": "dist/frameScheduling.esm.js", | ||
"es2015": "dist/frameScheduling.esm2015.js", | ||
"types": "dist/frameScheduling.d.ts", | ||
"repository": { | ||
@@ -25,16 +29,2 @@ "type": "git", | ||
}, | ||
"jest": { | ||
"moduleFileExtensions": [ | ||
"ts", | ||
"js" | ||
], | ||
"transform": { | ||
"^.+\\.ts$": "<rootDir>/node_modules/ts-jest/preprocessor.js" | ||
}, | ||
"timers": "fake", | ||
"mapCoverage": true, | ||
"testMatch": [ | ||
"<rootDir>/tests/**/*.(ts|js)" | ||
] | ||
}, | ||
"homepage": "https://github.com/Tom910/frame-scheduling#readme", | ||
@@ -47,6 +37,8 @@ "devDependencies": { | ||
"prettier": "1.7.4", | ||
"rollup": "^0.64.1", | ||
"rollup-plugin-typescript2": "^0.16.1", | ||
"ts-jest": "^21.1.4", | ||
"tslint": "^5.8.0", | ||
"typescript": "^2.5.3" | ||
"typescript": "^3.0.1" | ||
} | ||
} |
@@ -20,5 +20,150 @@ const context = typeof window !== "undefined" ? window : global; | ||
interface IListNode { | ||
interface QueueItem<T> { priority: number; value: T; } | ||
class PriorityUniqQueue<T> { | ||
private heapContainer: Array<QueueItem<T>>; | ||
private hashPriority: Record<string, T>; | ||
constructor() { | ||
this.heapContainer = []; | ||
this.hashPriority = Object.create(null); | ||
} | ||
public peek() { | ||
return this.heapContainer[0].value; | ||
} | ||
public poll() { | ||
let item; | ||
if (this.heapContainer.length === 1) { | ||
item = (this.heapContainer.pop() as QueueItem<T>); | ||
} else { | ||
item = this.heapContainer[0]; | ||
this.heapContainer[0] = (this.heapContainer.pop() as QueueItem<T>); | ||
this.heapifyDown(); | ||
} | ||
delete this.hashPriority[item.priority]; | ||
return item.value; | ||
} | ||
public add(priority: number, value: T) { | ||
this.heapContainer.push({ priority, value }); | ||
this.heapifyUp(); | ||
this.hashPriority[priority] = value; | ||
} | ||
public isEmpty() { | ||
return !this.heapContainer.length; | ||
} | ||
public get(priority: number) { | ||
return this.hashPriority[priority]; | ||
} | ||
public rising() { | ||
const keys = Object.keys(this.hashPriority); | ||
for (let i = keys.length; i > 0; i--) { | ||
const key = keys[i - 1]; | ||
this.hashPriority[Number(key) + 1] = this.hashPriority[key]; | ||
delete this.hashPriority[key]; | ||
} | ||
for (let j = 0; j < this.heapContainer.length; j++) { | ||
this.heapContainer[j].priority += 1; | ||
} | ||
} | ||
private heapifyUp(customStartIndex?: number) { | ||
let currentIndex = customStartIndex || this.heapContainer.length - 1; | ||
while ( | ||
this.hasParent(currentIndex) | ||
&& !this.pairIsInCorrectOrder( | ||
this.heapContainer[this.getParentIndex(currentIndex)], | ||
this.heapContainer[currentIndex], | ||
) | ||
) { | ||
this.swap(currentIndex, this.getParentIndex(currentIndex)); | ||
currentIndex = this.getParentIndex(currentIndex); | ||
} | ||
} | ||
private heapifyDown(customStartIndex?: number) { | ||
let currentIndex = customStartIndex || 0; | ||
let nextIndex = null; | ||
while (this.hasLeftChild(currentIndex)) { | ||
if ( | ||
this.hasRightChild(currentIndex) | ||
&& this.pairIsInCorrectOrder(this.rightChild(currentIndex), this.leftChild(currentIndex)) | ||
) { | ||
nextIndex = this.getRightChildIndex(currentIndex); | ||
} else { | ||
nextIndex = this.getLeftChildIndex(currentIndex); | ||
} | ||
if (this.pairIsInCorrectOrder( | ||
this.heapContainer[currentIndex], | ||
this.heapContainer[nextIndex], | ||
)) { | ||
break; | ||
} | ||
this.swap(currentIndex, nextIndex); | ||
currentIndex = nextIndex; | ||
} | ||
} | ||
private pairIsInCorrectOrder(firstElement: QueueItem<T>, secondElement: QueueItem<T>) { | ||
return firstElement.priority >= secondElement.priority; | ||
} | ||
private getLeftChildIndex(parentIndex: number) { | ||
return (2 * parentIndex) + 1; | ||
} | ||
private getRightChildIndex(parentIndex: number) { | ||
return (2 * parentIndex) + 2; | ||
} | ||
private getParentIndex(childIndex: number) { | ||
return Math.floor((childIndex - 1) / 2); | ||
} | ||
private hasParent(childIndex: number) { | ||
return this.getParentIndex(childIndex) >= 0; | ||
} | ||
private hasLeftChild(parentIndex: number) { | ||
return this.getLeftChildIndex(parentIndex) < this.heapContainer.length; | ||
} | ||
private hasRightChild(parentIndex: number) { | ||
return this.getRightChildIndex(parentIndex) < this.heapContainer.length; | ||
} | ||
private leftChild(parentIndex: number) { | ||
return this.heapContainer[this.getLeftChildIndex(parentIndex)]; | ||
} | ||
private rightChild(parentIndex: number) { | ||
return this.heapContainer[this.getRightChildIndex(parentIndex)]; | ||
} | ||
private swap(indexOne: number, indexTwo: number) { | ||
const tmp = this.heapContainer[indexTwo]; | ||
this.heapContainer[indexTwo] = this.heapContainer[indexOne]; | ||
this.heapContainer[indexOne] = tmp; | ||
} | ||
} | ||
interface ListNode { | ||
value: () => void; | ||
next: IListNode | null; | ||
next: ListNode | null; | ||
} | ||
@@ -28,7 +173,8 @@ | ||
private length: number; | ||
private head: IListNode | null; | ||
private last: IListNode; | ||
private head: ListNode | null; | ||
private last: ListNode | null; | ||
constructor() { | ||
this.head = null; | ||
this.last = null; | ||
this.length = 0; | ||
@@ -38,3 +184,3 @@ } | ||
public push(value: () => void) { | ||
const node: IListNode = { | ||
const node: ListNode = { | ||
next: null, | ||
@@ -48,3 +194,3 @@ value, | ||
} else { | ||
this.last.next = node; | ||
(this.last as ListNode).next = node; | ||
this.last = node; | ||
@@ -57,3 +203,3 @@ } | ||
public shift() { | ||
const currentHead = this.head as IListNode; | ||
const currentHead = this.head as ListNode; | ||
const value = currentHead.value; | ||
@@ -73,19 +219,5 @@ | ||
const frameScheduling = () => { | ||
const listJobs: { [l: string]: LinkedList } = {}; | ||
const heapJobs = new PriorityUniqQueue<LinkedList>(); | ||
let deferScheduled = false; | ||
let jobsSortCached: string[]; | ||
let jobsSortActual = false; | ||
const sortJobsByNumber = (jobs: object) => { | ||
if (!jobsSortActual) { | ||
jobsSortCached = Object.keys(jobs).sort( | ||
(left: string, right: string) => Number(left) - Number(right), | ||
); | ||
jobsSortActual = true; | ||
} | ||
return jobsSortCached; | ||
}; | ||
const runDefer = () => { | ||
@@ -100,20 +232,11 @@ if (!deferScheduled) { | ||
const addJob = (callback: () => void, priority: number) => { | ||
if (!listJobs[priority]) { | ||
listJobs[priority] = new LinkedList(); | ||
jobsSortActual = false; | ||
} | ||
listJobs[priority].push(callback); | ||
}; | ||
const getJob = heapJobs.get(priority); | ||
let newLinkedList; | ||
const raisingOfJob = () => { | ||
const keys = sortJobsByNumber(listJobs); | ||
for (let i = keys.length; i > 0; i--) { | ||
const key = keys[i - 1]; | ||
listJobs[Number(key) + 1] = listJobs[key]; | ||
delete listJobs[key]; | ||
if (!getJob) { | ||
newLinkedList = new LinkedList(); | ||
heapJobs.add(priority, newLinkedList); | ||
} | ||
jobsSortActual = false; | ||
((getJob || newLinkedList) as LinkedList).push(callback); | ||
}; | ||
@@ -123,10 +246,8 @@ | ||
const timeRun = Date.now(); | ||
let keysJobs = sortJobsByNumber(listJobs); | ||
while (true) { | ||
if (!keysJobs.length || Date.now() - timeRun > TIME_LIFE_FRAME) { | ||
if (heapJobs.isEmpty() || Date.now() - timeRun > TIME_LIFE_FRAME) { | ||
break; | ||
} else { | ||
const keyJob = keysJobs[keysJobs.length - 1]; | ||
const jobs = listJobs[keyJob]; | ||
const jobs = heapJobs.peek(); | ||
const job = jobs.shift(); | ||
@@ -141,5 +262,3 @@ | ||
if (jobs.isEmpty()) { | ||
delete listJobs[keyJob]; | ||
keysJobs.length = keysJobs.length - 1; | ||
jobsSortActual = false; | ||
heapJobs.poll(); | ||
} | ||
@@ -149,7 +268,6 @@ } | ||
keysJobs = sortJobsByNumber(listJobs); | ||
deferScheduled = false; | ||
if (!!keysJobs.length) { | ||
raisingOfJob(); | ||
if (!heapJobs.isEmpty()) { | ||
heapJobs.rising(); | ||
@@ -156,0 +274,0 @@ runDefer(); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
32558
844
10
8
1