Cooperative scheduler for the browser environment that provides time-slicing capabilities for JavaScript applications.
—
Performance monitoring and event logging capabilities for development and debugging of scheduler-based applications.
The profiling system provides detailed insights into task execution, timing, and scheduler behavior through structured event logging.
The main profiling interface provides methods to start and stop profiling event collection.
/**
* Profiling interface for performance monitoring
* Available only in profiling builds, null in production builds
*/
const unstable_Profiling: {
/** Start collecting profiling events */
startLoggingProfilingEvents(): void;
/** Stop collecting events and return logged data */
stopLoggingProfilingEvents(): ArrayBuffer | null;
} | null;Usage Examples:
import { unstable_Profiling } from "scheduler";
// Check if profiling is available
if (unstable_Profiling !== null) {
// Start profiling
unstable_Profiling.startLoggingProfilingEvents();
// Perform operations to profile
performScheduledWork();
// Stop profiling and get data
const profilingData = unstable_Profiling.stopLoggingProfilingEvents();
if (profilingData) {
// Process profiling data (ArrayBuffer)
analyzeProfilingData(profilingData);
}
} else {
console.log("Profiling not available in this build");
}
// Conditional profiling wrapper
function withProfiling(operation, name) {
const profilingEnabled = unstable_Profiling !== null;
if (profilingEnabled) {
unstable_Profiling.startLoggingProfilingEvents();
}
try {
return operation();
} finally {
if (profilingEnabled) {
const data = unstable_Profiling.stopLoggingProfilingEvents();
console.log(`Profiling data for ${name}:`, data);
}
}
}The profiling system logs events as structured data in an ArrayBuffer. Events are logged as 32-bit integers with the following format:
Event Types:
// Event type constants (internal)
const TaskStartEvent = 1; // Task added to queue
const TaskCompleteEvent = 2; // Task finished successfully
const TaskErrorEvent = 3; // Task threw an error
const TaskCancelEvent = 4; // Task was cancelled
const TaskRunEvent = 5; // Task started executing
const TaskYieldEvent = 6; // Task yielded control
const SchedulerSuspendEvent = 7; // Scheduler became idle
const SchedulerResumeEvent = 8; // Scheduler resumed workThe scheduler automatically logs various events during task execution when profiling is enabled:
import {
unstable_Profiling,
unstable_scheduleCallback,
unstable_NormalPriority
} from "scheduler";
async function profileTaskExecution() {
if (!unstable_Profiling) return;
unstable_Profiling.startLoggingProfilingEvents();
// Schedule multiple tasks to profile
const tasks = [];
for (let i = 0; i < 10; i++) {
const task = unstable_scheduleCallback(unstable_NormalPriority, (didTimeout) => {
// Simulate work
const start = performance.now();
while (performance.now() - start < 5) {
// Busy wait for 5ms
}
if (i % 3 === 0) {
// Some tasks yield
return (didTimeout) => {
console.log(`Task ${i} continuation, timeout: ${didTimeout}`);
};
}
});
tasks.push(task);
}
// Let tasks execute, then collect profiling data
setTimeout(() => {
const profilingData = unstable_Profiling.stopLoggingProfilingEvents();
console.log("Profiling completed, data size:", profilingData?.byteLength);
}, 1000);
}function analyzeProfilingData(arrayBuffer) {
if (!arrayBuffer || arrayBuffer.byteLength === 0) {
console.log("No profiling data available");
return;
}
const view = new Int32Array(arrayBuffer);
const events = [];
// Parse events from the buffer
for (let i = 0; i < view.length; i += 4) {
const eventType = view[i];
const timestamp = view[i + 1];
const taskId = view[i + 2];
const extraData = view[i + 3];
events.push({
type: getEventTypeName(eventType),
timestamp: timestamp / 1000, // Convert microseconds to milliseconds
taskId,
extraData
});
}
// Analyze the events
console.log(`Collected ${events.length} profiling events`);
const taskMetrics = calculateTaskMetrics(events);
console.log("Task metrics:", taskMetrics);
}
function getEventTypeName(eventType) {
const names = {
1: "TaskStart",
2: "TaskComplete",
3: "TaskError",
4: "TaskCancel",
5: "TaskRun",
6: "TaskYield",
7: "SchedulerSuspend",
8: "SchedulerResume"
};
return names[eventType] || "Unknown";
}
function calculateTaskMetrics(events) {
const taskStats = new Map();
events.forEach(event => {
if (!taskStats.has(event.taskId)) {
taskStats.set(event.taskId, {
startTime: null,
endTime: null,
runTime: null,
yields: 0,
errors: 0
});
}
const stats = taskStats.get(event.taskId);
switch (event.type) {
case "TaskStart":
stats.startTime = event.timestamp;
break;
case "TaskRun":
stats.runTime = event.timestamp;
break;
case "TaskComplete":
stats.endTime = event.timestamp;
break;
case "TaskYield":
stats.yields++;
break;
case "TaskError":
stats.errors++;
break;
}
});
return {
totalTasks: taskStats.size,
avgExecutionTime: calculateAverageExecutionTime(taskStats),
totalYields: Array.from(taskStats.values()).reduce((sum, stats) => sum + stats.yields, 0),
totalErrors: Array.from(taskStats.values()).reduce((sum, stats) => sum + stats.errors, 0)
};
}// Development-only profiling wrapper
function createProfilingWrapper() {
if (process.env.NODE_ENV !== "development" || !unstable_Profiling) {
return {
start: () => {},
end: () => {},
wrap: (fn) => fn
};
}
let isActive = false;
return {
start(label = "operation") {
if (isActive) return;
isActive = true;
console.log(`Starting profiling: ${label}`);
unstable_Profiling.startLoggingProfilingEvents();
},
end(label = "operation") {
if (!isActive) return;
isActive = false;
const data = unstable_Profiling.stopLoggingProfilingEvents();
console.log(`Profiling completed: ${label}`, data);
},
wrap(fn, label) {
return (...args) => {
this.start(label);
try {
return fn(...args);
} finally {
this.end(label);
}
};
}
};
}
// Usage
const profiler = createProfilingWrapper();
const profiledFunction = profiler.wrap(
() => {
// Scheduled work
performComplexOperation();
},
"complex-operation"
);Profiling is controlled at build time through feature flags:
unstable_Profiling is null)The profiling system adds minimal overhead when disabled, but can impact performance when enabled due to event logging overhead.
Install with Tessl CLI
npx tessl i tessl/npm-scheduler