During the NG2 development process, the angular team brought us a new library –zone.js. Zone.js's design is inspired by the Dart language, which describes the context of the JavaScript execution process and can be passed between asynchronous tasks in a persistent way, similar to the TLS (Thread-local storage: Thread-local storage) technology in Java. Zone.js is the implementation framework that introduces TLS into the JavaScript language.
So what problems can zone.js solve for us? Before answering this question, bloggers would like to look back on what challenges we have encountered in JavaScript development.
Problem Introduction
Let's take a look at the usual synchronous JavaScript code:
var foo = function(){ ... }, bar = function(){ ... }, baz = function(){ ... };foo();bar();baz();
There is nothing special about this code, and the order of execution is nothing special,:foo–> bar–> Baz in our prediction. It's also easy to do performance monitoring, and we just need to record the execution time before and after the execution context.
var start, timer = performance ? performance.now.bind(performance) : Date.now.bind(Date);start = timer();foo(); bar(); baz(); console.log(Math.floor((timer() - start) * 100) / 100 + ‘ms‘);
But in the world of JavaScript is not all that simple, well-known JavaScript single-threaded execution. So in order to not block the user experience of the UI interface, many of the time-consuming operations performed in JavaScript are encapsulated for asynchronous operations such as SetTimeout, XMLHttpRequest, Dom events, and so on. Due to the browser's boarding restrictions, the asynchronous operation in JavaScript is inherently characteristic and is deeply imprinted in the marrow. This is one of the reasons Dr. Ryan Dahl chose JavaScript to develop the node. JS platform. For JavaScript single-threaded execution you can refer to another Blogger blog post: A brief description of JavaScript single-threaded and browser event loops.
So how do we do performance monitoring for this asynchronous code?
var foo = function(){ setTimeout(..., 2000); }, bar = function(){ $.get(...).success(...); }, baz = function(){ ... };foo();bar();baz();
In this code, settimeout and Ajax asynchronous calls are introduced. Where Ajax callbacks and SetTimeout callback time sequences are difficult to determine, it is not as simple to introduce performance detection code to this code as it would be to execute code in the order above. If we need to force a performance test, we will insert the relevant hook code in the settimeout and $.get callbacks and record the execution time, so our business code will become very confusing, like a group of "Italian Ramen" (What the fuck! )。
Zone.js Introduction
At the beginning of this article, it is mentioned that Zone.js provides the execution context for JavaScript and can be passed between asynchronous tasks in a persistent pass. It's time for zone.js to play. Zone.js uses the brute force of Monkey Patches (monkey-patched) to wrap asynchronous tasks in JavaScript into a layer so that these asynchronous tasks will run in the context of the zone. Each asynchronous task is treated as a task in zone.js, and on the basis of a task Zone.js provides the developer with a hook function before and after execution. These hook functions include:
- onzonecreated: A hook function that produces a new zone object. Zone.fork also produces a new zone that inherits from the base class zone, forming a separate zone context;
- Beforetask:zone the hook function before task execution;
- Aftertask:zone the hook function after the completion of the task execution;
- Onerror:zone the exception hook function when running task;
And Zone.js encapsulates most of the asynchronous events in JavaScript, including:
- Zone.alert;
- Zone.prompt;
- Zone.requestanimationframe, Zone.webkitrequestanimationframe, Zone.mozrequestanimationframe;
- Zone.addeventlistener;
- Zone.addeventlistener, Zone.removeeventlistener;
- Zone.settimeout, Zone.cleartimeout, zone.setimmediate;
- Zone.setinterval, Zone.clearinterval
As well as the promise, geolocation positioning information, websocket and so on also carried out package, you can find them here https://github.com/angular/zone.js/tree/master/lib/patch.
Let's consider a simple zone.js example:
var log = function(phase){ return function(){ console.log("I am in zone.js " + phase + "!"); };};zone.fork({ onZoneCreated: log("onZoneCreated"), beforeTask: log("beforeTask"), afterTask: log("afterTask"),}).run(function(){ var methodLog = function(func){ return function(){ console.log("I am from " + func + " function!"); }; }, foo = methodLog("foo"), bar = methodLog("bar"), baz = function(){ setTimeout(methodLog(‘baz in setTimeout‘), 0); }; foo(); baz(); bar();});
The output of executing this sample code is:
I am in zone.js beforeTask!I am from foo function!I am from bar function!I am in zone.js afterTask!I am in zone.js onZoneCreated!I am in zone.js beforeTask!I am from baz in setTimeout function!I am in zone.js afterTask!
From the above output, we can see that in Zone.js the Run method block is divided into two tasks, which are the task of the method body runtime and the asynchronous SetTimeout task respectively. And we are able to intercept and do some meaningful things before and after the creation of these tasks.
In Zone.js, the fork method produces a subclass that inherits to a zone, and in the fork function you can configure a specific hook method to form a separate zone context. The Run method is the external interface that initiates the execution of the business code.
The zone also supports parent-child inheritance, and it also defines a set of DSL grammars that support the $, +,-prefix.
- $ will pass the parent class zone's hook function, which facilitates the control of the zone hook function execution;
- -Represents the function of this hook before the hook function of the parent zone;
- + instead, this hook function is represented after the hook function of the parent zone
For more syntax use, refer to zone.js GitHub home document Https://github.com/angular/zone.js.
Introduction of Zone.js
With these basic knowledge about zone.js, we can solve the problem left over from the beginning of this article. The following code is a sample code from the Zone.js project: https://github.com/angular/zone.js/blob/master/example/profiling.html
var profilingZone = (function () { var time = 0, timer = performance ? performance.now.bind(performance) : Date.now.bind(Date); return { beforeTask: function () { this.start = timer(); }, afterTask: function () { time += timer() - this.start; }, time: function () { return Math.floor(time*100) / 100 + ‘ms‘; }, reset: function () { time = 0; } }; }()); zone.fork(profilingZone).run(function(){ //业务逻辑代码 });
This starts the time calculation in Beforetask and calculates the current cumulative time spent in Aftertask. So we can use Zone.time () at any time in the logic of the business code to get the current time-consuming.
The realization of Zone.js
Knowing the time of Zone.js, perhaps you will feel like me as magic, how is it achieved?
Here is the code snippet for browser.ts in Zone.js (https://github.com/angular/zone.js/blob/master/lib/patch/browser.ts):
export function apply () {fnpatch.patchsetclearfunction (Global, Global. Zone, [[' SetTimeout ', ' cleartimeout ', false, false], [' setinterval ', ' clearinterval ', true, false], [' Setimmedia Te ', ' clearimmediate ', false, false], [' Requestanimationframe ', ' cancelanimationframe ', false, True], [' Mozrequestan Imationframe ', ' mozcancelanimationframe ', false, True], [' Webkitrequestanimationframe ', ' webkitcancelanimationframe ', false, true]]); Fnpatch.patchfunction (Global, [' Alert ', ' Prompt ']); Eventtargetpatch.apply (); Propertydescriptorpatch.apply (); Promisepatch.apply (); Mutationobserverpatch.patchclass (' Mutationobserver '); Mutationobserverpatch.patchclass (' Webkitmutationobserver '); Definepropertypatch.apply (); Registerelementpatch.apply (); Geolocationpatch.apply (); Filereaderpatch.apply ();}
From here we can see that zone.js settimeout, SetInterval, setimmediate, and events, promise, and geo-information geolocation in the browser have been specially handled. So how are these treatments handled? Here is the implementation code for Fnpatch.patchsetclearfunction, from Zone.js functions.ts (https://github.com/angular/zone.js/blob/master/ Lib/patch/functions.ts) Code snippet:
Export function patchsetclearfunction (window, Zone, fnnames) {function Patchmacrotaskmethod (setName, Clearname, repeating, Israf) {//Browser native method retained var setnative = Window[setname]; var clearnative = Window[clearname]; var ids = {}; if (setnative) {var wtfseteventfn = wtf.createevent (' zone# ' + setName + ' (UInt32 Zone, UInt32 ID, uint32 delay) '); var wtfcleareventfn = wtf.createevent (' zone# ' + clearname + ' (UInt32 Zone, UInt32 id) '); var wtfcallbackfn = Wtf.createscope (' ZONE#CB: ' + setName + ' (UInt32 Zone, UInt32 ID, uint32 delay) '); Package for browser native method Window[setname] = function () {return global.zone[setname].apply (global.zone, arguments); }; Package package for Browser native method Window[clearname] = function () {return global.zone[clearname].apply (global.zone, arguments) ; }; Create your own wrapping method, transferred here by Wind[setname] to execute. Zone.prototype[setname] = function (FN, delay) {var callbackfn = fn; if (typeof Callbackfn!== ' function ') {//force the error by calling the method with wrong args setnative.apply (window, arguments); } var zone = this; var setId = null; Wrap the callback function into the zone. Arguments[0] = function () {var callbackzone = Zone.isrootzone () | | | israf? zone:zone.fork (); var callbackthis = this; var Callbackargs = arguments; Return Wtf.leavescope (WTFCALLBACKFN (callbackzone. $id, SetId, delay), Callbackzone.run (function ( {if (!repeating) {delete Ids[setid]; Callbackzone.removetask (CALLBACKFN); } return Callbackfn.apply (Callbackthis, Callbackargs); }) ); }; if (repeating) {zone.addrepeatingtask (CALLBACKFN); } else {zone.addtask (CALLBACKFN); } setId = setnative.apply (window, arguments); Ids[setid] = callbACKFN; WTFSETEVENTFN (zone. $id, setId, delay); return setId; }; ...... } } fnnames.foreach (function (args) {patchmacrotaskmethod.apply (null, args); });};
In the above code, the native method of the browser is first saved in setnative so that it will be reused. Immediately after Zone.js began its violent behavior, overwriting window[setname] and Window[clearname] and then the call to SetName was transferred to its own zone[setname], Zone.js is so violent. The interception transfer is implemented for browser native objects. It then invokes its own addrepeatingtask, AddTask, and WTF events before and after the task executes to apply all the hook functions on the registration.
To believe that as a reader you have understood the zone.js mechanism, is not the same as the author of a "Simple rough" feeling? But it is really powerful, for us to implement the asynchronous task tracking, analysis and so on.
Zone.js Application Scenarios
Zone.js can implement asynchronous task tracking, analysis, error logging, development and debug tracking, these are the application scenarios of the zone.js scene. You can also see more sample code in Https://github.com/angular/zone.js/tree/master/example, as well as Brian's lecture video on Zone.js in ng-conf 2014: https:/ /www.youtube.com/watch?v=3iqtmusce_u.
Of course, for some specific business analysis Zone.js also has its very good use of the scene. If you have used Angular1 development, then perhaps you can still remember: using third-party events or Ajax but forget $scope. $apply scene. In Angular1, changing the data model,angular in a non-angular context is unpredictable and does not trigger an update of the interface. So we have to show the call to $scope. $apply or $timeout to trigger an update of the interface. Angular framework in order to learn more about the events of change, it has to encapsulate a framework of built-in services and directives, such as Ngclick, Ngchange, $http, $timeout, which also increases the learning cost of ANGULAR1.
In order to solve some of the Angular1 problems, the ANGULAR2 team introduced Zone.js, abandoning the custom service and instructions, instead embracing the native objects and methods of the browser. So in ANGULAR2 can use any browser event, only need to identify the parentheses template syntax: (eventName), equivalent to On-eventname, you can also directly use the browser's native object, such as SetTimeout, AddEventListener, promise, fetch and so on.
Of course, zone.js can also be used in ANGULAR1 projects. The sample code is as follows (Http://jsbin.com/kenilivuvi/edit?html,js,output):
angular.module("com.ngbook.demo", []) .controller("DemoController", [‘$scope‘, function($scope){ zone.fork({ afterTask: function(){ var phase = $scope.$root.$$phase; if([‘$apply‘, ‘$digest‘].indexOf(phase) === -1) { $scope.$apply(); } } }).run(function(){ setTimeout(function(){ $scope.fromZone = "I am from zone with setTimeout!"; }, 2000); }); }]);
In the sample code, the $scope will be tried after each task's completion. $apply, forcing changes to the model data to be updated to the UI interface. More places to use zone.js in ANGULAR1 should be in Directive, and the zone creation process can be encapsulated as a service (factory method, each time a new zone object is returned). The same zone package is also found in the ANGULAR2, which is called Ngzone (https://github.com/angular/angular/blob/master/modules/angular2/src/core/zone /ng_zone.ts).
Zone.js-The beauty of violence