Back to Angular

Zone.js's support for standard apis

packages/zone.js/STANDARD-APIS.md

22.0.0-next.1013.2 KB
Original Source

Zone.js's support for standard apis

Zone.js patches most standard APIs, such as DOM event listeners and XMLHttpRequest in the browser, as well as EventEmitter and the fs API in Node.js, so they can operate within a zone.

In this document, all patched API are listed.

For non-standard APIs, please see NON-STANDARD-APIS.md

Patch Mechanisms

There are several patch mechanisms:

  • Wrap: Makes callbacks run in zones, allowing applications to receive onInvoke and onIntercept callbacks.
  • Task: Similar to the JavaScript VM, applications can receive onScheduleTask, onInvokeTask, onCancelTask, and onHasTask callbacks:
    1. MacroTask
    2. MicroTask
    3. EventTask

Some APIs which should be treated as Tasks, but are currently still patched in the wrap way.

Browser

Web APIs

APIPatch MechanismOthers
setTimeout/clearTimeoutMacroTaskapp can get handlerId, interval, args, isPeriodic(false) through task.data
setImmediate/clearImmediateMacroTasksame with setTimeout
setInterval/clearIntervalMacroTaskisPeriodic is true, so setInterval will not trigger onHasTask callback
requestAnimationFrame/cancelAnimationFrameMacroTask
mozRequestAnimationFrame/mozCancelAnimationFrameMacroTask
webkitRequestAnimationFrame/webkitCancelAnimationFrameMacroTask
alertwrap
promptwrap
confirmwrap
PromiseMicroTask
EventTargetEventTasksee below Event Target for more details
HTMLElement on propertiesEventTasksee below on properties for more details
XMLHttpRequest.send/abortMacroTask
XMLHttpRequest on propertiesEventTask
IDBIndex on propertiesEventTask
IDBRequest on propertiesEventTask
IDBOpenDBRequest on propertiesEventTask
IDBDatabaseRequest on propertiesEventTask
IDBTransaction on propertiesEventTask
IDBCursor on propertiesEventTask
WebSocket on propertiesEventTask
MutationObserverwrap
WebkitMutationObserverwrap
FileReaderwrap
registerElementwrap

EventTarget

  • For browsers supporting EventTarget, Zone.js just patches EventTarget, so everything that inherits from EventTarget will also be patched.
  • For browsers that do not support EventTarget, Zone.js will patch the following APIs in the IDL that inherit from EventTarget
ApplicationCacheEventSourceFileReaderInputMethodContext
MediaControllerMessagePortNodePerformance
SVGElementInstanceSharedWorkerTextTrackTextTrackCue
TextTrackListWebKitNamedFlowWindowWorker
WorkerGlobalScopeXMLHttpRequestXMLHttpRequestEventTargetXMLHttpRequestUpload
IDBRequestIDBOpenDBRequestIDBDatabaseIDBTransaction
IDBCursorDBIndexWebSocket

The following on properties, such as onclick, onreadystatechange, are patched in zone.js as EventTasks:

copycutpasteabort
blurfocuscanplaycanplaythrough
changeclickcontextmenudblclick
dragdragenddragenterdragleave
dragoverdragstartdropdurationchange
emptiedendedinputinvalid
keydownkeypresskeyupload
loadeddataloadedmetadataloadstartmessage
mousedownmouseentermouseleavemousemove
mouseoutmouseovermouseuppause
playplayingprogressratechange
resetscrollseekedseeking
selectshowstalledsubmit
suspendtimeupdatevolumechangewaiting
mozfullscreenchangemozfullscreenerrormozpointerlockchangemozpointerlockerror
errorwebglcontextrestoredwebglcontextlostwebglcontextcreationerror

NodeJS

APIPatch MechanismOthers
setTimeout/clearTimeoutMacroTaskapp can get handlerId, interval, args, isPeriodic(false) through task.data
setImmediate/clearImmediateMacroTasksame with setTimeout
setInterval/clearIntervalMacroTaskisPeriodic is true, so setInterval will not trigger onHasTask callback
process.nextTickMicrotaskisPeriodic is true, so setInterval will not trigger onHasTask callback
PromiseMicroTask
EventEmitterEventTaskAll APIs inherit EventEmitter are patched as EventTask
cryptoMacroTask
fsMacroTaskall async methods are patched

EventEmitter, addEventListener, prependEventListener, and on will be patched once as EventTasks, while removeEventListener and removeAllListeners will remove those EventTasks.

Electron

Zone.js does not patch the Electron API. However, in Electron, both browser APIs and Node APIs are patched. Therefore, if you want to include Zone.js in an Electron application, please use dist/zone-mix.js.

ZoneAwareError

ZoneAwareError replaces the global Error and adds zone information to the stack trace. It also addresses the 'this' issue. This problem can occur when creating an error without new: this will be undefined in strict mode and global in non-strict mode, potentially leading to difficult-to-detect issues.

js
const error = Error();

ZoneAwareError makes sure that this is ZoneAwareError even without new.

ZoneAwarePromise

ZoneAwarePromise wraps the global Promise and allows it to run in zones as a MicroTask. It also passes mostly all Promise A+ tests.

UnpatchedEvents

Event unpatching means calling the native implementation immediately, without any additional functionality such as task tracking, zone capturing, etc.

Sometimes we don't want certain events to be patched by zone.js. We can instruct zone.js to leave these events.

This can be done through __Zone_ignore_on_properties and __zone_symbol__UNPATCHED_EVENTS, which should be available on the window object. It is usually recommended to declare these lists in the <head> element before your code is loaded. Once the zone.js code is executed and all the patches are initialized, the __Zone_ignore_on_properties and __zone_symbol__UNPATCHED_EVENTS lists will no longer be read:

html
<head>
  <script>
    // Disable patching `on` properties for the following targets:
    const targets = [window, Document.prototype, HTMLBodyElement.prototype, HTMLElement.prototype];

    // This is declared without `let` or `const`, so it will become a property
    // of the global `window` object:
    __Zone_ignore_on_properties = [];

    // For the following targets, the `onscroll` property will not be patched.
    // Therefore, when using `window.onscroll = () => {}`, the callback will
    // always be invoked inside the root zone, because the implementation is not
    // patched and the task is not tracked for the current target:
    targets.forEach((target) => {
      __Zone_ignore_on_properties.push({
        target: target,
        ignoreProperties: ['scroll'],
      });
    });

    // When calling `addEventListener` with the `scroll` event, `zone.js` will
    // invoke the native browser `addEventListener` implementation immediately:
    __zone_symbol__UNPATCHED_EVENTS = ['scroll'];
  </script>
</head>

When we declare events in __zone_symbol__UNPATCHED_EVENTS, their callbacks will not be intercepted by zone.js, since zone.js calls the native addEventListener of the EventTarget (not the patched function):

js
__zone_symbol__UNPATCHED_EVENTS = ['scroll'];

Zone.current.fork({name: 'child'}).run(() => {
  window.addEventListener('scroll', () => {
    console.log(Zone.current); // <root>
  });
});

Note that for __Zone_ignore_on_properties, we need to specify object prototypes (like Document.prototype), with window being the exception. This is because on properties are part of the prototypes.

When the target is Document.prototype and ignoreProperties is a list containing ['scroll'], then document.onscroll will not be patched.

Note that __Zone_ignore_on_properties allows for individually unpatching events, but this only applies to on properties. In contrast, __zone_symbol__UNPATCHED_EVENTS affects all events with the specified name. For example, if you're dealing with a library that uses FileReader and sets the load event using reader.onload = () => { ... }, you can simply use __Zone_ignore_on_properties = [{ target: FileReader.prototype, ignoreProperties: ['load'] }]. However, if the library adds a load event listener using addEventListener, you would need to use __zone_symbol__UNPATCHED_EVENTS = ['load'], which would affect other targets as well. For instance, the load event of XMLHttpRequest would also be unpatched.