Frontend Development 8 min read

Mastering Zone.js: Isolate Execution Contexts and Run Multiple jQuery Versions

This article explains how Zone.js, the core Angular 2 component inspired by NodeJS Domain and Dart Zone, can be used to manage execution contexts for both synchronous and asynchronous code, enabling advanced scenarios such as running different jQuery versions side‑by‑side in a single application.

Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Mastering Zone.js: Isolate Execution Contexts and Run Multiple jQuery Versions

Zone.js is the core component that the Angular team created for Angular 2, taking inspiration from NodeJS's Domain and Dart's Zone.

At first I rejected Zone.js because I thought its only purpose was asynchronous error tracking, which I didn't need. However, the

execution context

can do much more than just error tracking.

Understanding the execution context

<code>Zone.current.fork({}).run(function () {
    Zone.current.inTheZone = true;

    setTimeout(function () {
        console.log('in the zone: ' + !!Zone.current.inTheZone); // 'in the zone: true'
    }, 0);
});

console.log('in the zone: ' + !!Zone.current.inTheZone); // 'in the zone: false'
</code>
We can think of an execution context as the context that is only related to the Zone instance created by fork .

The example shows that only inside the forked Zone we set

Zone.current.inTheZone

to

true

, so the log outside the Zone prints

false

.

How does it work?

Imagine the process as synchronous; the following code demonstrates creating a new Zone and switching back.

<code>const defaultZone = Zone.current
// create a new Zone
const zone = new Zone()
// set current zone
Zone.current = zone
// set a value in the current zone
Zone.current.inTheZone = true
console.log('in the zone: ' + !!Zone.current.inTheZone)
// exit the current zone
Zone.current = defaultZone
console.log('in the zone: ' + !!Zone.current.inTheZone)
</code>

For asynchronous code, we simply add a guard at each async entry point.

<code>const defaultZone = Zone.current
// generate a new Zone
const zone = new Zone()
// set current zone
Zone.current = zone
Zone.current.inTheZone = true
const anonymousA = function () {
  console.log('in the zone: ' + !!Zone.current.inTheZone); // 'in the zone: true'
}
// map the function to the zone
anonymousA._zone = zone
// exit current zone
Zone.current = defaultZone
setTimeout(() => {
  // restore zone when the function runs
  Zone.current = anonymousA._zone
  anonymousA.call(this)
  // exit again
  Zone.current = defaultZone
}, 0)
console.log('in the zone: ' + !!Zone.current.inTheZone)
</code>

Of course, the real implementation of

Zone.js

is far more complex; interested readers can explore the source code.

Using different jQuery versions in the same project

From the previous examples we see that we can store properties on a Zone instance that are only visible inside that Zone. By leveraging

Object.defineProperty

we can achieve the goal of isolating different library versions.

First, we write a very simple module executor (just a demo, not production‑ready):

<code>!function (win, Zone) {
    var map = {};
    var noop = {};
    var dependence = {};
    var alias = {};
    var hasSet = {};

    // only supports define(name, factory) for demo purposes
    function define(name, factory) {
        if (typeof factory === 'function') {
            map[name] = { factory: factory, exports: noop };
        } else {
            map[name] = { exports: factory };
        }
    }

    function require(name) {
        var module = map[name];
        if (module.exports !== noop) return module.exports;
        if (dependence[name]) {
            var properties = {};
            // map window.xxx -> require('xxx')
            Object.keys(dependence[name]).forEach(function (key) {
                var res = alias[key] || key;
                properties[res] = require(key + '@' + dependence[name][key]);
                if (!hasSet[res]) {
                    hasSet[res] = true;
                    Object.defineProperty(window, res, {
                        get: function () { return Zone.current.get(res); }
                    });
                }
            });
            // fork a Zone for each module execution
            Zone.current.fork({ properties: properties }).run(function () {
                module.exports = module.factory();
            });
        } else {
            module.exports = module.factory();
            return module.exports;
        }
    }

    function config(opt) {
        Object.assign(dependence, opt.dep);
        Object.assign(alias, opt.alias);
    }

    require.config = config;
    window.define = define;
    window.require = require;
}(window, Zone);
</code>

Try it out:

<code>// simulate two jQuery versions
define('[email protected]', { version: '1.4', bind: function () { console.log('call bind'); } })
define('[email protected]', { version: '1.8', on: function () { console.log('call on'); } })

// just log the version, do nothing else
function logVersion() { console.log('version === ', $.version) }

// first module uses 1.8
define('module1', function module1() {
    $.on();
    setTimeout(logVersion, 100);
})

// second module uses 1.4
define('module2', function module2() {
    $.bind();
    setTimeout(logVersion, 300);
})

require.config({
    dep: {
        module1: { 'jquery': '1.8' },
        module2: { 'jquery': '1.4' }
    },
    alias: { 'jquery': '$' }
})
require('module1')
require('module2')
</code>

For the full implementation see the two-different-jquery repository.

Going further

Based on Zone.js we can build a sandbox that allows multiple technology stacks to coexist in a large legacy application without nasty conflicts.

We can also create a generic dependency‑injection solution that fully decouples modules from each other.

Execution ContextAngularjQueryZone.js
Tencent IMWeb Frontend Team
Written by

Tencent IMWeb Frontend Team

IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.