This week, while working on the upload-to-ownCloud app I'm making through my OPW internship, I learned to use the $q service, a promise/deferred library built into Angular. The $q service is fairly straightfoward, but integrating it with another asynchronous API — the API that accesses the Firefox OS file system — was more difficult than I expected.

Generally, promises are pretty straightforward. When you call a function that returns a promise, it gives you a mysterious box that you can't peek into (yet). The mysterious box doesn't yet have anything in it, but at some point, the asynchronous things happening somewhere out in the aether of your application will put a value in that box, and then callback you've attached (via "then") to it will be fired.

var mysteriousBox = functionReturningPromise();
mysteriousBox.then(function(contents) {
        alert('Surprise! The contents of the box is' + contents);
});

That's not so bad. But how do we structure functionReturningPromise? Unfortunately, many of the existing examples for using promises assume that you're working with APIs that automatically return a promise (like Angular's $http service). If your API does automatically return a promise, lucky you! However, different JavaScript APIs deal with asynchronicity in a variety of ways, so it's not uncommon to have to adapt an asynchronous API that's not promise based to work with a promise-based API.

For example, Firefox OS's Device Storage API doesn't pair with Angular's $q in a particularly obvious way. The enumerate() function returns a cursor object that allows asynchronous iteration through the files in a device storage object. Here's an example similar to the one given in the MDN documentation:

var pics = navigator.getDeviceStorage('pictures');

var cursor = pics.enumerate()

cursor.onsuccess = function () {
    console.log(this.result.name);

    if (!this.done) {
    this.continue();
    }
}

The cursor object allows the programmer to move through a list of results asynchronously. (I often think of cursors as iterators, but they're not totally analogous.) When the cursor is called — initially, or with continue() — it attempts to retrieve the next item, and then calls either its "onerror" or "onsuccess" method, depending on the result of that attempt. In the example, the "onsuccess" function checks for a picture, logs the name of the picture, and then continues if the cursor's "ready state" is "done." Calling continue() begins the pseudo-recursive cycle again by attempting to retrieve the next item.

To make this work with $q, I needed to create a deferred object, and then add points in the code where the deferred object could either resolve or reject the promise. Since a check to see if the cursor wasn't done didn't give me a natural place to resolve the request, I reversed the logic check and resolved the promise when the "done" check passed.

var deferred = $q.defer();

// make an array to send back with the promise
var photos = [];

// access the file system and make a cursor to page through the files
var files = navigator.getDeviceStorage('pictures');
var cursor = files.enumerate();

cursor.onsuccess = function() {
   if (this.done) {
   // populate the mysterious box with "photos"
      deferred.resolve(photos);
   }

   else {
      photos.push(this.result.name);
      this.continue();
   }
};

// In real life, we'd need more error checking than just
// checking to see if we accessed the file system, but for now...
cursor.onerror = function() {
   deferred.reject("Error browsing the file system.");
};

// send the promise to whomever has 
// requested it by calling this function
return deferred.promise;

Here, I push photo names onto the "photos" array as the cursor iterates through file storage, and then resolve the promise by sending the "photos" object only when the cursor's list of files has been exhausted. Likewise, the promise is rejected when the cursor encounters an error.

The most important thing to note here, though, is that the return of the actual promise object occurs outside of the asynchronous code that interacts with Mozilla's API. The mysterious box is sent to the code that "catches" the promise instantaneously, regardless of whether the asynchronous code above that's interacting with the filesystem is finished running. The beauty of promises is that it's okay to send the empty box, as long as you can ensure that one of your callbacks will either populate that box or reject the promise later.