scheduling
Advanced tools
Comparing version 1.4.3 to 1.4.4
@@ -1,1 +0,1 @@ | ||
let e=window.requestAnimationFrame,n=60,t=1,r=0,o=0,f=0;const i=new Map,u=[],a=[];let c=[],s=[],d=0;function l(t){n=t,e=t=>{requestAnimationFrame((r=>{const o=1e3/n,i=r-f;i>=o?t(r):setTimeout((()=>e(t)),o-i)}))}}r=performance.now(),function d(l){!function(){let e,o=0;for(let[e,n]of i)null!=n&&n.func(n.args);for(;c.length>0;)e=c.pop(),e.func(e.args);for(o=0;o<u.length;o++)e=u[o],r-e.time>e.delay/t&&(e.func(e.args),e.repeat?e.time=r:u.splice(o,1));let f=performance.now();for(;a.length>0;){if(e=a.shift(),!(performance.now()-f<1e3/n*t)){a.unshift(e);break}e.func(e.args)}}(),f=r,void 0===l?r+=1e3/n*t:r=l,o=r-f,c=c.concat(s),s=[],e(d)}(r);var p={addEF:function(e,n){if("function"!=typeof e)throw new Error("Invalid function provided for enterframe task.");const t=++d;return i.set(t,{func:e,args:n}),t},removeEF:function(e){return i.delete(e),-1},delay:function(e,n,t,o=!1){if("function"!=typeof e)throw new Error("Invalid function provided for delayed task.");u.push({func:e,args:t,delay:n,time:r,repeat:o})},next:function(e,n){if("function"!=typeof e)throw new Error("Invalid function provided for next frame task.");s.push({func:e,args:n})},defer:function(e,n){if("function"!=typeof e)throw new Error("Invalid function provided for deferred task.");a.push({func:e,args:n})},getTime:function(){return r/1e3},getDeltaTime:function(){return o},setFrameRate:l,setTimeScale:function(e){t=e,l(n*t)},getTimeScale:function(){return t},setEnterframeFunc:function(n){e=n}};export default p; | ||
let e=window.requestAnimationFrame,n=60,t=1,r=0,o=0,i=0,f=0,u=0;const c=new Map,a=[],s=new Set;let l=[],d=[],m=!1,p=0;function w(e){return c.delete(e)}function g(t){if(t<=0||!Number.isFinite(t))throw new Error("Frame rate must be a positive number");n=t,e=t=>{requestAnimationFrame((r=>{const o=1e3/n,f=r-i;f>=o?t(r):setTimeout((()=>e(t)),o-f)}))}}function h(e=!1){if(m&&!e)return;let o,i=0;for(let[e,n]of c)null!=n&&n.func(n.args);for(;l.length>0;)o=l.pop(),o.func(o.args);for(i=0;i<a.length;i++)o=a[i],r-o.time>o.delay/t&&(o.func(o.args),o.repeat?o.time=r:(a.splice(i,1),i--));let f=performance.now();for(;s.length>0;){if(o=s.shift(),!(performance.now()-f<1e3/n*t)){s.unshift(o);break}o.func(o.args)}}function v(e){if(i=r,void 0===e)u+=1e3/n*t,r=u;else{if(!m){0===f&&(f=e);u+=e-f,f=e}r=u}o=r-i,l=l.concat(d),d=[]}r=performance.now(),function n(t){h(),v(t),e(n)}(r);var E={addEF:function(e,n){if("function"!=typeof e)throw new Error("Invalid function provided for enterframe task.");const t=++p;return c.set(t,{func:e,args:n}),{id:t,cancel:()=>{w(t)}}},removeEF:w,delay:function(e,n,t,o=!1){if("function"!=typeof e)throw new Error("Invalid function provided for delayed task.");const i=++p,f={id:i,func:e,args:t,delay:n,time:r,repeat:o,cancelled:!1};return a.push(f),{cancel:()=>{const e=a.findIndex((e=>e.id===i));-1!==e&&a.splice(e,1)}}},next:function(e,n){if("function"!=typeof e)throw new Error("Invalid function provided for next frame task.");d.push({func:e,args:n})},defer:function(e,n){if("function"!=typeof e)throw new Error("Invalid function provided for deferred task.");s.add({func:e,args:n})},getTime:function(){return r/1e3},getDeltaTime:function(){return o},setFrameRate:g,setTimeScale:function(e){if(e<0||!Number.isFinite(e))throw new Error("Time scale must be a non-negative number");t=e,g(n*t)},getTimeScale:function(){return t},setEnterframeFunc:function(n){e=n},step:function(){h(!0),v()},pause:function(){m=!0,f=0},resume:function(){m=!1},isPaused:function(){return m},removeAllTasks:function(){c.clear(),a.length=0,s.clear(),l.length=0,d.length=0,u=0,r=0}};export default E; |
141
dev/main.js
@@ -5,92 +5,77 @@ // main.js | ||
const maxFrame = 60; | ||
let count = 0; | ||
let countAnim = 0; | ||
let taskId; | ||
let startTime = performance.now(); | ||
function log() { | ||
console.log( | ||
"Count", | ||
count++, | ||
Scheduler.getTime(), | ||
performance.now(), | ||
Scheduler.getDeltaTime() | ||
); | ||
if (count > maxFrame) { | ||
Scheduler.removeEF(taskId); | ||
} | ||
} | ||
function pauseTest() { | ||
const efTask = Scheduler.addEF(() => { | ||
console.log("loop", Scheduler.getTime()); | ||
}); | ||
function animFrame() { | ||
console.log("Animation Frame", countAnim); | ||
if (countAnim++ < maxFrame) { | ||
requestAnimationFrame(animFrame); | ||
} | ||
} | ||
window.addEventListener("keydown", (e) => { | ||
if (e.key === "p") { | ||
if (Scheduler.isPaused()) { | ||
Scheduler.resume(); | ||
} else { | ||
Scheduler.pause(); | ||
} | ||
} else if (e.key === "s") { | ||
Scheduler.step(); | ||
} | ||
}); | ||
Scheduler.setFrameRate(30); | ||
// Scheduler.setTimeScale(0.5); | ||
taskId = Scheduler.addEF(log); | ||
// animFrame(); | ||
const delayTask = Scheduler.delay(() => { | ||
console.log("delayTask"); | ||
}, 1000); | ||
function delayCall(mArgs) { | ||
console.log( | ||
"Delayed call", | ||
Scheduler.getTime(), | ||
performance.now() - startTime | ||
); | ||
setTimeout(() => { | ||
console.log("cancel delayTask"); | ||
delayTask.cancel(); | ||
}, 500); | ||
} | ||
// Scheduler.delay(delayCall, 1000); | ||
setTimeout(() => { | ||
console.log("1s"); | ||
}, 1000); | ||
setTimeout(() => { | ||
console.log("2s"); | ||
}, 2000); | ||
pauseTest(); | ||
// window.setTimeout(delayCall, 1000 / 60); | ||
function timeScaleTest() { | ||
const maxFrame = 60; | ||
let count = 0; | ||
let countAnim = 0; | ||
let taskId; | ||
let startTime = performance.now(); | ||
function log() { | ||
console.log( | ||
"Count", | ||
count++, | ||
Scheduler.getTime(), | ||
performance.now(), | ||
Scheduler.getDeltaTime() | ||
); | ||
if (count > maxFrame) { | ||
Scheduler.removeEF(taskId); | ||
} | ||
} | ||
/* | ||
import Scheduler from "../build/scheduler"; | ||
let count = 0; | ||
let efIndex; | ||
function logMessage(msg) { | ||
console.log(msg, Scheduler.getDeltaTime(), Scheduler.getTime()); | ||
} | ||
function log(msg) { | ||
logMessage(msg ? msg : "-"); | ||
if (count++ > 60) { | ||
Scheduler.removeEF(efIndex); | ||
function animFrame() { | ||
console.log("Animation Frame", countAnim); | ||
if (countAnim++ < maxFrame) { | ||
requestAnimationFrame(animFrame); | ||
} | ||
} | ||
} | ||
// efIndex = Scheduler.addEF(log); | ||
// Scheduler.delay(log, "Delayed task - ", 2000); | ||
Scheduler.setFrameRate(30); | ||
// Scheduler.setTimeScale(0.5); | ||
taskId = Scheduler.addEF(log); | ||
// animFrame(); | ||
function deferTest(o) { | ||
let t = 0; | ||
while (t < o.target) { | ||
t += Math.random(); | ||
function delayCall(mArgs) { | ||
console.log( | ||
"Delayed call", | ||
Scheduler.getTime(), | ||
performance.now() - startTime | ||
); | ||
} | ||
} | ||
for (let i = 0; i < 10; i++) { | ||
const t = 600000; | ||
const target = Math.floor(t + Math.random() * t); | ||
Scheduler.defer(deferTest, { name: "task" + i, target }); | ||
// Scheduler.delay(delayCall, 1000); | ||
setTimeout(() => { | ||
console.log("1s"); | ||
}, 1000); | ||
setTimeout(() => { | ||
console.log("2s"); | ||
}, 2000); | ||
} | ||
function loop(mArgs) { | ||
console.log("Args", count, mArgs, performance.now()); | ||
if (count++ < 60) { | ||
requestAnimationFrame(loop); | ||
} | ||
} | ||
loop(); | ||
*/ |
{ | ||
"name": "scheduling", | ||
"version": "1.4.3", | ||
"version": "1.4.4", | ||
"description": "A enterframe tool", | ||
@@ -5,0 +5,0 @@ "main": "build/scheduler.js", |
@@ -7,2 +7,4 @@ let enterframeFunc = window.requestAnimationFrame; | ||
let prevTime = 0; | ||
let lastTimestamp = 0; | ||
let accumulatedTime = 0; | ||
@@ -12,5 +14,6 @@ // tasks | ||
const delayTasks = []; | ||
const deferTasks = []; | ||
const deferTasks = new Set(); | ||
let highTasks = []; | ||
let nextTasks = []; | ||
let paused = false; | ||
@@ -25,3 +28,3 @@ // indexing | ||
* @param {object} mArgs the arguments for the function | ||
* @returns {number} the id of the task | ||
* @returns {object} An object containing the task id and cancel method | ||
* @throws {Error} Throws an error if the provided mFunc is not a function. | ||
@@ -36,3 +39,9 @@ */ | ||
enterframeTasks.set(id, { func: mFunc, args: mArgs }); | ||
return id; | ||
return { | ||
id, | ||
cancel: () => { | ||
removeEF(id); | ||
}, | ||
}; | ||
} | ||
@@ -44,8 +53,6 @@ | ||
* @param {number} mIndex the id of the task to be removed | ||
* @returns {number} return -1 | ||
* @returns {boolean} true if task was found and removed, false otherwise | ||
*/ | ||
function removeEF(mIndex) { | ||
enterframeTasks.delete(mIndex); | ||
return -1; | ||
return enterframeTasks.delete(mIndex); | ||
} | ||
@@ -60,2 +67,3 @@ | ||
* @param {bool} mRepeat if the task should repeat | ||
* @returns {object} An object containing the cancel method | ||
* @throws {Error} Throws an error if the provided mFunc is not a function. | ||
@@ -68,3 +76,5 @@ */ | ||
delayTasks.push({ | ||
const taskId = ++idTable; | ||
const task = { | ||
id: taskId, | ||
func: mFunc, | ||
@@ -75,3 +85,15 @@ args: mArgs, | ||
repeat: mRepeat, | ||
}); | ||
cancelled: false, | ||
}; | ||
delayTasks.push(task); | ||
return { | ||
cancel: () => { | ||
const index = delayTasks.findIndex((t) => t.id === taskId); | ||
if (index !== -1) { | ||
delayTasks.splice(index, 1); | ||
} | ||
}, | ||
}; | ||
} | ||
@@ -105,3 +127,3 @@ | ||
} | ||
deferTasks.push({ func: mFunc, args: mArgs }); | ||
deferTasks.add({ func: mFunc, args: mArgs }); | ||
} | ||
@@ -115,2 +137,6 @@ | ||
function setFrameRate(mFrameRate) { | ||
if (mFrameRate <= 0 || !Number.isFinite(mFrameRate)) { | ||
throw new Error("Frame rate must be a positive number"); | ||
} | ||
frameRate = mFrameRate; | ||
@@ -137,2 +163,6 @@ | ||
function setTimeScale(mTimeScale) { | ||
if (mTimeScale < 0 || !Number.isFinite(mTimeScale)) { | ||
throw new Error("Time scale must be a non-negative number"); | ||
} | ||
timeScale = mTimeScale; | ||
@@ -162,6 +192,50 @@ | ||
/** | ||
* Pauses the scheduler, stopping time progression and task processing. | ||
* When paused, the scheduler will not process tasks except through manual stepping. | ||
* Resets the lastTimestamp to ensure accurate time tracking when resumed. | ||
*/ | ||
function pause() { | ||
paused = true; | ||
lastTimestamp = 0; | ||
} | ||
/** | ||
* Resumes the scheduler from a paused state. | ||
* When resumed, the scheduler will continue processing tasks and updating time. | ||
* Time accumulation will continue from the point where it was paused. | ||
*/ | ||
function resume() { | ||
paused = false; | ||
} | ||
/** | ||
* Checks if the scheduler is currently paused. | ||
* | ||
* @returns {boolean} True if the scheduler is paused, false otherwise. | ||
*/ | ||
function isPaused() { | ||
return paused; | ||
} | ||
/** | ||
* Advances the scheduler by one frame while paused. | ||
* This allows for manual control of the scheduler's progression. | ||
* Will process tasks and update time exactly once, regardless of pause state. | ||
* | ||
* @example | ||
* Scheduler.pause(); | ||
* Scheduler.step(); // Advances one frame | ||
*/ | ||
function step() { | ||
process(true); | ||
updateTime(); | ||
} | ||
/** | ||
* Processes and executes tasks from the enterframeTasks array. | ||
* Iterates through each task in the enterframeTasks array and calls the task's function with its arguments if the task is not null or undefined. | ||
*/ | ||
function process() { | ||
function process(mForce = false) { | ||
if (paused && !mForce) return; | ||
let i = 0; | ||
@@ -186,3 +260,2 @@ let task; | ||
task = delayTasks[i]; | ||
// console.log(currentTime, task.time, task.delay); | ||
if (currentTime - task.time > task.delay / timeScale) { | ||
@@ -194,2 +267,3 @@ task.func(task.args); | ||
delayTasks.splice(i, 1); // Remove non-repeating task | ||
i--; // Adjust index after removal | ||
} | ||
@@ -237,8 +311,23 @@ } | ||
process(); | ||
updateTime(mTimestamp); | ||
enterframeFunc(loop); | ||
} | ||
function updateTime(mTimestamp) { | ||
prevTime = currentTime; | ||
if (mTimestamp === undefined) { | ||
currentTime += (1000 / frameRate) * timeScale; | ||
accumulatedTime += (1000 / frameRate) * timeScale; | ||
currentTime = accumulatedTime; | ||
} else { | ||
currentTime = mTimestamp; | ||
if (!paused) { | ||
if (lastTimestamp === 0) { | ||
lastTimestamp = mTimestamp; | ||
} | ||
const deltaMs = mTimestamp - lastTimestamp; | ||
accumulatedTime += deltaMs; | ||
lastTimestamp = mTimestamp; | ||
} | ||
currentTime = accumulatedTime; | ||
} | ||
@@ -251,3 +340,2 @@ | ||
nextTasks = []; | ||
enterframeFunc(loop); | ||
} | ||
@@ -259,2 +347,32 @@ | ||
function cleanup() { | ||
enterframeTasks.clear(); | ||
delayTasks.length = 0; | ||
deferTasks.clear(); | ||
highTasks.length = 0; | ||
nextTasks.length = 0; | ||
} | ||
function removeAllTasks() { | ||
cleanup(); | ||
accumulatedTime = 0; | ||
currentTime = 0; | ||
} | ||
// Batch processing for tasks | ||
function processBatch(tasks, timeLimit) { | ||
const startTime = performance.now(); | ||
const processed = []; | ||
for (const task of tasks) { | ||
if (performance.now() - startTime > timeLimit) { | ||
break; | ||
} | ||
processed.push(task); | ||
task.func(task.args); | ||
} | ||
return processed; | ||
} | ||
export default { | ||
@@ -272,2 +390,7 @@ addEF, | ||
setEnterframeFunc, | ||
step, | ||
pause, | ||
resume, | ||
isPaused, | ||
removeAllTasks, | ||
}; |
47334
146
15
883
0