Appcelerator Blog

The Leading Resource for All Things Mobile

Understanding Execution Contexts

0 Flares 0 Flares ×

A couple of questions have surfaced from community members recently with regard to execution contexts in a Titanium Mobile application. I thought I would take a few minutes to explain execution contexts to folks for whom the concept is still a little foggy. First, let me explain what I mean when I say “execution context”.

A JavaScript application running in a web browser is single threaded and has a global variable scope for the entire application. A Titanium Mobile application is similar, except that a single application may have multiple JavaScript processes running at once, each with it’s own unique scope. We call these “execution contexts”.

Every Titanium Mobile application has at least one execution context, the one created by the app.js JavaScript program which bootstraps your entire application. New execution contexts are created when a window is created that points to an external JavaScript file, which would then bootstrap the new window. We refer to windows that have their own execution contexts as “heavyweight” windows (making those that do not “lightweight” windows, since a window need not point to an external JavaScript file). The syntax for creating a heavyweight window is below, and familiar to the majority of you I am sure:

var win = Titanium.UI.createWindow({

title: ‘New Window’,

url: ‘win.js’

});

When this new window is opened, it will have it’s own context and scope, so any variables declared in other contexts will not be available. Say that my app.js file contains the following code:

var awesome = true; //this is visible in the current execution context

var win = Ti.UI.createWindow({

title: 'New Window',

url: 'win.js',

backgroundColor:'#fff'

});

win.open();

And win.js contains the following code:

var label = Ti.UI.createLabel({

text: (awesome) ? 'JavaScript is awesome!' : 'Yeah, well so's your old lady!'

});

Ti.UI.currentWindow.add(label);

This example would fail because the code in win.js is not executed in the same context as the code in app.js, so the variable awesome is out of scope.

Occasionally, it is necessary for one context to communicate with another. This is accomplished using the custom events facility that is built into the Titanium API. You can fire application-level events that will be received in all currently active execution contexts (app.js plus any open windows) via fireEvent. Custom events can have arbitrary data passed along with them, as shown in the example below:

Ti.App.fireEvent(‘myCustomEvent’, {

myCustomEventValue: ‘someValue’

});

You can listen for these custom events in any context by using addEventListener at the application level as well. The example below listens for our custom event – you’ll notice that any properties of the object passed as the second argument to fireEvent are available on the event object the callback function takes as an argument:

Ti.App.addEventListener(‘myCustomEvent’, function(event) {

Ti.API.info(‘You sent me: ‘+event.myCustomEventValue);

});

Note that only ACTIVE execution contexts will receive a custom event when it is sent, so any windows that are not yet open will not receive the event.

While not every application requires multiple execution contexts, it is good to know when execution contexts are created and how they work. Hope that helps, and let us know if you have any questions.

0 Flares Twitter 0 Facebook 0 Google+ 0 LinkedIn 0 Email -- 0 Flares ×

14 Comments

  1. Adam Lea

    Thank you Kevin. That makes thing MUCH clearer than in the video training. I really appreciate this.

    Adam

  2. Thanks Kevin. This is a good overview.

    One thing for everyone to note is that all UI operations still happen on a single, UI thread. So, even when you have multiple JS contexts in different threads, scopes, etc. Titanium will still do all UI operations like drawing, etc on the UI thread.

  3. Anjo

    You can also say win.awesome = awesome when opening and check in win.js for Ti.UI.currentWindow.awesome. This works better for by-reference items, though.

  4. William Xue

    Thanks a lot Kevin!

    I was wondering if there is any real benefit of having multiple execution contexts?

    Any advantages particularly comparing to a giant app.js file that uses a lot of Ti.include(xx)?

  5. Good question – there’s no functional advantage other than isolating scripts from one another. One could very easily write an app in that fashion- Snapost, one our demo apps, is written in just that way. In that case, it is important to use closures where necessary to avoid polluting the global scope with variables that should be “private”.

  6. When you use Ti.include(‘win.js’), I assume that all the stuff inside is included in the current execution context, correct?

    Do event listeners create new execution contexts, or would something like this work just fine?

    var cool = ‘hi’;
    myButton.addEventListener(‘click’,function(){
    Ti.API.info(cool);
    });

  7. Kevin Whinnery

    @Clifton – correct – Ti.include is similar to a script tag in HTML. The code in the script is immediately executed in the current context.

    Event listeners do not create their own execution context, but they do have their own scope. In your example, the variable cool would be available within the scope of the closure, so your log statement would work. On the flipside, if you had:

    myButton.addEventListener(“click”, function(e) {
    var cool = ‘cool’;
    });

    Ti.API.info(cool);

    Obviously, this would not work, since the variable was declared within the scope of the closure.

  8. Makes sense. Thanks, Kevin.

  9. @kevin w:
    do you have plans to add an include_once or similar, so an include is only loaded once?

  10. William Xue

    Thanks for the answer Kevin.

  11. KP

    Kevin,

    Thanks for the explanation. Can you put this in the context of managing a user session? In the application I am building I need to store a session key which is used in all subsequent calls to our web services API. I am using Tab Groups with their own execution contexts and am looking for the best strategy to reference the global variable:

    1. Declare a session facade that reads and writes data to the properties API
    2. Read and write directly to the properties API
    3. Manage authentication in app.js and pass the sessionkey to each window (mywin.sessionkey = 12345)

    This is where an application skeleton would really come in handy.

    Thanks,
    Kai

  12. Kevin Whinnery

    @Kai,

    Any of those approaches would work – I would lean toward reading and writing directly from App.Properties unless you’re likely to want to track other session information. If at any point you need more than one persistent bit of session data, then I would create a session facade that you might include from an external file in all the contexts which get created by your app.

  13. Pratesi maurizio

    If I use Ti.APP.Properties.setString(‘key’,’value’) in a window called win1, and I close this win1.

    After that I open a new window called win2. What return Ti.APP.Properties.getString(‘key’, ‘null’) within win2? Return the string ‘null’ or return the string ‘value’?

  14. Amplop Keren

    I try to stress test, by changing win.js code to
    for (var i=0;i<999999;i++) {for (var j=0;j<999999;j++) {Ti.API.info(j);}}

    then all the event listener on app.js will freeze

    is this fake multiple JavaScript processes running at once?
    thx u

Comments are closed.

Sign up for updates!

Become a mobile leader. Take the first step to scale mobile innovation throughout your enterprise.
Get in touch
computer and tablet showing Appcelerator software
Start free, grow from there.
0 Flares Twitter 0 Facebook 0 Google+ 0 LinkedIn 0 Email -- 0 Flares ×