Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save spankyj/9701751 to your computer and use it in GitHub Desktop.
Save spankyj/9701751 to your computer and use it in GitHub Desktop.

When you want to use a asynchronous loading technique (like requireJS for instance) in a Windows HTML application you can have trouble catching the activation event due to the fact that WinJS.Application.start() must be called synchronously at the start of the process in order to register with the system's activation handler at the right time.

Here is a code snippet which describes a way to work around that by using an artifical event to stall the WinJS.Application event queue until your activation handler is registered and ready to run.

    (function () {
        "use strict";

        // Alias the application object
        var app = WinJS.Application;

        // When using require and you need to wait to register your activated event handler
        // until the dependent modules have loaded you still need to call app.start() 
        // synchronously at startup, in order to facilitate this we take advantage of the
        // ordering characteristics and extensibility of the WinJS.Application event
        // queue and introduce a (arbitrary) "wait" event that blocks the queue until
        // your activation handler is registered.
        //
        // First we create a promise which will be used to block the queue and smuggle
        // out the complete handler so we can call it later.
        var activateRegistered;
        var activateRegisteredPromise = new WinJS.Promise(function (c) {
            activateRegistered = c;
        });
        //
        // Then we register a "wait" event handler which simply blocks on the promise.
        app.addEventListener("wait", function (args) {
            args.setPromise(activateRegisteredPromise);
        });
        //
        // Then we queue a "wait" event ahead of activation (before app.start()).
        app.queueEvent({ type: "wait" });
        //
        // Calling app.start makes the application model register for the system's activated
        // event and dispatches the "wait" event we have queued.
        app.start();

        // Use require to load modules asynchonrously
        require(["log"], function (log) {
            var logger = log.logger("app");

            // When you have modules loaded register your activated event handler
            app.onactivated = function (args) {
                // Do whatever you want in your activation handler...
            };

            // Then after your activated handler has been registered complete the
            // outstanding promise which will unblock the queue and allow other events
            // to pump including the queued up activation event.
            activateRegistered();
        });

    })();
@pke
Copy link

pke commented Mar 22, 2014

One more thing. I have rewritten my log code to create unique log file names for each activation kind. So that I end up with ms-appdata://local/launch-yyyy-mm-dd.log and ms-appdata://local/fileOpenPicker-yyyy-mm-dd.log.
At the moment I do that in my onactivated handler i a slightly different variant using the launch promise:

(function () {
  "use strict";

  var app = WinJS.Application;
  WinJS.Binding.optimizeBindingReferences = true;

  var sched = WinJS.Utilities.Scheduler;
  var ui = WinJS.UI;
  var nav = WinJS.Navigation;

  app.onactivated = function activated(args) {
    // Optimize the load of the application and while the splash screen is shown, execute high priority scheduled work.
    ui.disableAnimations();
    var p = new WinJS.Promise(function (c, e, p) {
      var activationKindName;
      for (var key in Windows.ApplicationModel.Activation.ActivationKind) {
        if (args.detail.kind == [Windows.ApplicationModel.Activation.ActivationKind[key]]) {
          activationKindName = key;
          break;
        }
      }

      WinJS.Namespace.define("app", {
        activationKindName: activationKindName
      })

      require(["log", "navigator"], function (log, navigator) {
        var logger = log.logger("app");
        logger.trace("activated with: " + JSON.stringify(args.detail));
        require(["app/" + activationKindName], function (app) {
          ui.processAll().then(function () {
            if (!app.activate || !app.activate(args)) {
              return nav.navigate(nav.location || Application.navigator.home, {
                args: args
              });
            }
          }).then(function () {
            return sched.requestDrain(sched.Priority.aboveNormal + 1);
          }).then(function () {
            ui.enableAnimations();
          }).then(c,e,p);
        });
      });
    });
    args.setPromise(p);
  };

  app.start();

})();

As you can see I export the kind of activation in "app.activationKindName" and only then require the log module which will then use that to compose the log file name. Also see how I chained my activation promises completion, error and progress handler in the last then statement.

About the splashscreens I think that could also be automated using a timeout promise to display a splashscreen after seconds. And yes, I have also seen many JS apps that include all their JS in their initial page and not load it on demand.

One more thing. I used the new 8.1 skeleton for activation handling:

// Optimize the load of the application and while the splash screen is shown, execute high priority scheduled work.
ui.disableAnimations();
ui.processAll().then(function () {
  return nav.navigate(nav.location || Application.navigator.home, nav.data);
}).then(function () {
  return sched.requestDrain(sched.Priority.aboveNormal + 1);
}).then(function () {
  ui.enableAnimations();
}).then(c,e,p);

2 questions: Which things could be scheduled during startup? The animations seems to stay disabled when any of the promises fails.

@pke
Copy link

pke commented Mar 23, 2014

Also it seems not possible to put this kind of activation code into the file specified in data-main, since this file is also asynchronously loaded by requirejs and therefore misses the WebUI activated event. So we are stuck with 2 script tags in WinJS apps. One that includes require.js and the other that contains the wiring code to the apps activated handling. But from there on its modules all the way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment