Execution context library that persists across asynchronous operations for JavaScript applications
Asynchronous task scheduling and management system that categorizes async work into microtasks, macrotasks, and event tasks.
Core task interface that represents all asynchronous work in zones.
/**
* Base interface for all asynchronous tasks in zones
*/
interface Task {
/** Task type: 'microTask', 'macroTask', or 'eventTask' */
readonly type: TaskType;
/** Current execution state of the task */
state: TaskState;
/** Debug identifier indicating the API that scheduled the task */
readonly source: string;
/** Zone context where the task will execute */
readonly zone: Zone;
/** User callback function to execute */
callback: Function;
/** Task-specific data and configuration */
data?: TaskData;
/** Custom scheduler function for the task */
scheduleFn?: (task: Task) => void;
/** Custom cancellation function for the task */
cancelFn?: (task: Task) => void;
/** VM execution function (internal) */
invoke: Function;
/** Number of times the task has been executed */
runCount: number;
/** Cancel the scheduling request for this task */
cancelScheduleRequest(): void;
}
/** Task type enumeration */
type TaskType = 'microTask' | 'macroTask' | 'eventTask';
/** Task state enumeration */
type TaskState = 'notScheduled' | 'scheduling' | 'scheduled' | 'running' | 'canceling' | 'unknown';Tasks that execute immediately after the current execution stack, before yielding to the browser.
/**
* MicroTask - executes immediately after current stack
* Examples: Promise.then(), queueMicrotask(), MutationObserver
*/
interface MicroTask extends Task {
type: 'microTask';
}Usage Examples:
import 'zone.js';
const myZone = Zone.current.fork({ name: 'micro-zone' });
// Schedule microtask directly
const microTask = myZone.scheduleMicroTask('custom-micro', () => {
console.log('MicroTask executed in:', Zone.current.name);
});
// Promise.then() creates microtasks automatically
myZone.run(() => {
Promise.resolve().then(() => {
console.log('Promise microtask in:', Zone.current.name); // 'micro-zone'
});
});Tasks that execute after a delay or in the next event loop iteration.
/**
* MacroTask - executes after delay or in next event loop
* Examples: setTimeout(), setInterval(), XMLHttpRequest
*/
interface MacroTask extends Task {
type: 'macroTask';
}Usage Examples:
import 'zone.js';
const timerZone = Zone.current.fork({ name: 'timer-zone' });
// Schedule macrotask directly
const macroTask = timerZone.scheduleMacroTask(
'custom-timer',
() => console.log('MacroTask executed'),
{ delay: 1000 }, // TaskData with delay
(task) => {
// Custom scheduler
task.data.handleId = setTimeout(task.invoke, task.data.delay);
},
(task) => {
// Custom canceller
clearTimeout(task.data.handleId);
}
);
// setTimeout creates macrotasks automatically
timerZone.run(() => {
setTimeout(() => {
console.log('Timer macrotask in:', Zone.current.name); // 'timer-zone'
}, 500);
});Tasks that handle events and may execute multiple times.
/**
* EventTask - handles events, may execute multiple times
* Examples: addEventListener(), on(), once()
*/
interface EventTask extends Task {
type: 'eventTask';
}Usage Examples:
import 'zone.js';
const eventZone = Zone.current.fork({ name: 'event-zone' });
// Schedule event task directly
const eventTask = eventZone.scheduleEventTask(
'custom-event',
(event) => console.log('Event handled:', event.type),
{ passive: true }, // TaskData with event options
(task) => {
// Custom scheduler - add event listener
document.addEventListener('click', task.invoke, task.data);
},
(task) => {
// Custom canceller - remove event listener
document.removeEventListener('click', task.invoke, task.data);
}
);
// addEventListener creates event tasks automatically
eventZone.run(() => {
document.getElementById('button')?.addEventListener('click', () => {
console.log('Click event in:', Zone.current.name); // 'event-zone'
});
});Configuration data specific to different task types.
/**
* Task-specific configuration data
*/
interface TaskData {
/** Whether the task repeats automatically (setInterval) */
isPeriodic?: boolean;
/** Whether the task can be manually rescheduled */
isRefreshable?: boolean;
/** Delay in milliseconds before task execution */
delay?: number;
/** Native handle ID (setTimeout return value) */
handleId?: number;
/** Generic handle for the task */
handle?: any;
/** Additional task-specific properties */
[key: string]: any;
}Usage Examples:
// Periodic task data
const periodicData: TaskData = {
isPeriodic: true,
delay: 1000,
handleId: 0
};
// Event task data
const eventData: TaskData = {
passive: true,
once: false,
capture: true
};
// Custom task data
const customData: TaskData = {
priority: 'high',
retries: 3,
timeout: 5000
};Tasks progress through various states during their lifecycle.
/**
* Task execution states
*/
type TaskState =
| 'notScheduled' // Task created but not yet scheduled
| 'scheduling' // Task is being scheduled
| 'scheduled' // Task is scheduled and waiting to execute
| 'running' // Task is currently executing
| 'canceling' // Task is being cancelled
| 'unknown'; // Task state is unknown
/**
* Task state information for zone notifications
*/
type HasTaskState = {
/** Whether zone has pending microtasks */
microTask: boolean;
/** Whether zone has pending macrotasks */
macroTask: boolean;
/** Whether zone has pending event tasks */
eventTask: boolean;
/** Type of task that caused the state change */
change: TaskType;
};Usage Examples:
const taskTrackingZone = Zone.current.fork({
name: 'task-tracker',
onHasTask: (delegate, current, target, hasTaskState) => {
console.log('Task state changed:');
console.log('- MicroTasks pending:', hasTaskState.microTask);
console.log('- MacroTasks pending:', hasTaskState.macroTask);
console.log('- EventTasks pending:', hasTaskState.eventTask);
console.log('- Change type:', hasTaskState.change);
},
onScheduleTask: (delegate, current, target, task) => {
console.log(`Scheduling ${task.type}:`, task.source);
console.log('- Current state:', task.state);
return delegate.scheduleTask(target, task);
},
onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => {
console.log(`Executing ${task.type}:`, task.source);
console.log('- Run count:', task.runCount);
return delegate.invokeTask(target, task, applyThis, applyArgs);
}
});Zone provides methods for directly scheduling different types of tasks.
/**
* Schedule a MicroTask in the zone
* @param source - Debug identifier for the task
* @param callback - Function to execute
* @param data - Optional task configuration data
* @param customSchedule - Optional custom scheduler function
* @returns Created MicroTask
*/
scheduleMicroTask(
source: string,
callback: Function,
data?: TaskData,
customSchedule?: (task: Task) => void
): MicroTask;
/**
* Schedule a MacroTask in the zone
* @param source - Debug identifier for the task
* @param callback - Function to execute
* @param data - Optional task configuration data
* @param customSchedule - Optional custom scheduler function
* @param customCancel - Optional custom canceller function
* @returns Created MacroTask
*/
scheduleMacroTask(
source: string,
callback: Function,
data?: TaskData,
customSchedule?: (task: Task) => void,
customCancel?: (task: Task) => void
): MacroTask;
/**
* Schedule an EventTask in the zone
* @param source - Debug identifier for the task
* @param callback - Function to execute
* @param data - Optional task configuration data
* @param customSchedule - Optional custom scheduler function
* @param customCancel - Optional custom canceller function
* @returns Created EventTask
*/
scheduleEventTask(
source: string,
callback: Function,
data?: TaskData,
customSchedule?: (task: Task) => void,
customCancel?: (task: Task) => void
): EventTask;
/**
* Schedule an existing Task (useful for rescheduling cancelled tasks)
* @param task - Task to schedule
* @returns The scheduled task
*/
scheduleTask<T extends Task>(task: T): T;
/**
* Cancel a scheduled Task
* @param task - Task to cancel
* @returns Cancellation result
*/
cancelTask(task: Task): any;Usage Examples:
const schedulingZone = Zone.current.fork({ name: 'scheduler' });
// Custom microtask
const microTask = schedulingZone.scheduleMicroTask(
'custom-micro',
() => console.log('Custom microtask executed'),
{ priority: 1 }
);
// Custom macrotask with cancellation
const macroTask = schedulingZone.scheduleMacroTask(
'custom-macro',
() => console.log('Custom macrotask executed'),
{ delay: 2000 },
(task) => {
// Custom scheduler
task.data.handleId = setTimeout(() => {
task.invoke();
}, task.data.delay);
},
(task) => {
// Custom canceller
if (task.data.handleId) {
clearTimeout(task.data.handleId);
}
}
);
// Cancel the macrotask
setTimeout(() => {
schedulingZone.cancelTask(macroTask);
console.log('MacroTask cancelled');
}, 1000);
// Reschedule a task
const rescheduledTask = schedulingZone.scheduleTask(microTask);Zone specifications can intercept task operations through lifecycle hooks.
/**
* Hook called when tasks are scheduled
*/
onScheduleTask?: (
parentZoneDelegate: ZoneDelegate,
currentZone: Zone,
targetZone: Zone,
task: Task
) => Task;
/**
* Hook called when tasks are executed
*/
onInvokeTask?: (
parentZoneDelegate: ZoneDelegate,
currentZone: Zone,
targetZone: Zone,
task: Task,
applyThis: any,
applyArgs?: any[]
) => any;
/**
* Hook called when tasks are cancelled
*/
onCancelTask?: (
parentZoneDelegate: ZoneDelegate,
currentZone: Zone,
targetZone: Zone,
task: Task
) => any;
/**
* Hook called when zone task state changes
*/
onHasTask?: (
parentZoneDelegate: ZoneDelegate,
currentZone: Zone,
targetZone: Zone,
hasTaskState: HasTaskState
) => void;Install with Tessl CLI
npx tessl i tessl/npm-zone-js