Thursday, August 9, 2012

Promises: jQuery deferred object vs WinJS Promise

When I was adjusting my Linq2IndexedDB library to enable Windows 8 development, I had some little issues porting the jQuery promises to WinJS promises. In this post I will show you some differences I had issues with and how I fixed them.

Passing context

The first problem I ran into: WinJS promises doesn’t support passing a context. Because I don’t take advantage of the context yet, this wasn’t an issue for me yet. I easily solved it by writing a wrapper around the 2 promises. In case of the jQuery promise, the context gets passed. And in case of the WinJS promise, I just ignore it for the moment and hope it will get implemented in the future.

   1: function promiseWrapper(promise) {
   2:     if (isMetroApp) {
   3:         return new WinJS.Promise(function(completed, error, progress){
   4:             promise({
   5:                 complete: function (context, args) {
   6:                     completed(args);
   7:                 },
   8:                 error: function (context, args) {
   9:                     error(args);
  10:                 },
  11:                 progress: function (context, args) {
  12:                     progress(args);
  13:                 }
  14:             });
  15:         });
  16:     } else if (typeof ($) === "function" && $.Deferred) {
  17:         return $.Deferred(function (dfd) {
  18:             promise({
  19:                 complete: function (context, args) {
  20:                     dfd.resolveWith(context, [args]);
  21:                 },
  22:                 error: function (context, args) {
  23:                     dfd.rejectWith(context, [args]);
  24:                 },
  25:                 progress: function (context, args) {
  26:                     dfd.notifyWith(context, [args]);
  27:                 }
  28:             });
  29:         }).promise();
  30:     }
  31: }

Passing multiple parameters

A second problem I had was the fact that the WinJS only allows one argument to be passed when calling a complete, error or progress callback. Because I needed to pass multiple values in my library, I needed to rewrite every complete, error and progress method I called. Instead of just passing multiple arguments to the callback methods, I needed to wrap the arguments into an array so they could be passed as single argument.

But that wasn’t enough. Because the jQuery promise is smart enough to convert an array of arguments into a callback with multiple arguments, I needed to wrap the array of arguments into an other array (If you look in the sample above, you will see in case of the jQuery promise, brackets (‘[]’) were added around the args argument.). I needed to do this, because it was the only way to get the same signature when working with the WinJS & jQuery promise.

   1: promiseWrapper(function (pw) {
   2:     pw.complete(context, [arg1, arg2]);
   3: });

Progress Event Doesn’t fire in Winjs promise

The last issue I suffered was the fact that a progress event in didn’t fire in some cases. After a little investigation, I came to the conclusion that I was calling the progress event, before the promise object got created. I first noticed this when I was creating a transaction on the IndexedDB API. When a transaction was created, I fired a progress event with the transaction data. In other methods, where I needed the transaction, I used this the progress call to execute my queries. Once the transaction was committed, the complete event got fired. Because the progress call never got called, the query was never executed. This way the transaction was immediately committed and the complete callback got called without any action taken.

To fix this I delayed the progress call a little bit. By adding a setTimeout of 1 ms I noticed my problem was solved, and my progress events got called.

   1: if (isMetroApp) {
   2:     setTimeout(function () {
   3:         var txn = db.transaction(objectStoreNames, transactionType); 
   4:         txn.oncomplete = function (e) {
   5:             pw.complete(txn, [txn, e]);
   6:         };   
   7:         pw.progress(txn, [txn]);
   8:     }, 1);
   9: }


As seen above working with the WinJS is a slice different of working with the deferred object in jQuery. But with the given workarounds, it is possible to solve the most issues. I hope that Microsoft will take a look at the jQuery deferred object in the future and add some of the jQuery capabilities (context, multiple arguments) into the WinJS promises. And hopefully the progress bug gets solved, so the ugly setTimeout can disappear in my code.


  1. hi

    why after call select method in version 1.0.12+ return value is undefined?

    db.linq.from("table1").select().then(function (e) {
    e >> undefined

    it's because nested properties feature in select method?

  2. Tnx for mentioning. I have fixed it and if you take the 1.0.15 version it should be solved.

    The bug was the fact I provide a context and a result in the callback. I forgot that in the complete of the select and provided only one argument to the callback (the context). That is why you got undifined, because the second argument wasn't provided.