in Appcelerator Titanium, Mobile, Web

The Promise of JavaScript Promises

A couple of years ago, I started a project for a customer developing a cross platform mobile that utilized their custom backend.  The cross platform framework in this case was Appcelerator Titanium, though the following could apply to any framework, mobile or web, since we are just talking JavaScript.

As APIs go, custom or otherwise, there were some complexities we had to deal with, most notably having to do several requests in order to capture all the data needed to perform specific functions in the mobile app.

Needing to do multiple asynchronous HttpClient requests at once meant we were going to have to do some work to track all of those callbacks, and it became apparent that the new concept of Promises would be beneficial to use.

When setting up the library that would be handling the fetching of data from the custom backend, I realized that I had not done a very good job of explaining to my teammates what I was attempting to do

To fix this, I set up a doc to try to both share my understanding of Promises, and describe my intent for implementing them in the main API library. JavaScript Promises were fairly new at the time, and my knowledge of them wasn’t very advanced, nor is it now, so if you see something that doesn’t add up, let me know!

Goals

I tried to make a set of guidelines for our library to aspire to, and considered these goals more than hard and fast rules:

  • I would like that we make all public apis (functions) in the main or related libraries return a promise and fulfill the data request through that promise
  • Promises should start at the source
  • So in functions making xhr (HttpClient) requests, the promise starts in the request.
  • Usually no need to create a new promise in the public/calling function, just pass on the existing one from the source
  • Even synchronous ‘source’ functions should use Promises (in other words, be async)
  • This makes it possible to use them in the promise chain
  • Will also keep us sane
  • Catch potential errors where feasible
  • Can be done at the end of the promise chain
  • At the very least, we should be checking console logs for unhandled errors reported by the promise library (we have some now)

Background

Promises are useful in making it simpler (after figuring out how they work, of course) to deal with asynchronous requests or actions, such as when getting or posting data to a server.

About Async

A server request is async because it takes time for the response to come back from the server, and our program may need to do other things in the meantime. JavaScript, and apps built in Titanium, can pretty much only do one thing at a time, we have a single thread to work with.

A single thread doing stuff synchronously, in order.

//step 1
var x = 1;

Then after that, we do

//step 2
x = x + 1;

And so on, just like normal.

Now, pretending we are doing a synchronous server call. If we do

//step 1
var x = synchronousCallToServer();
//step 2
x = x + 1;
//step 3
showNewWindow();

Step 2 and 3 won’t happen until step 1 completes, and if this were a server request, we may be waiting several seconds, during which our app is unresponsive, or blocked from performing additional tasks. No button clicks, nothing.

Async with Callbacks

We get around this issue by making an asynchronous request

function addNumbers(response){
  var x = response + 1;
};

//step 1
asyncCallToServer(addNumbers);

//step 2
showNewWindow();

In this example, Step 1 executes, then Step 2 executes immediately, with no waiting for Step 1 to finish. The addNumbers function is what we are calling a callback, and it is passed as an argument in the asynchronous call to the server. When that server request completes, the result is passed to our callback function and then executed.

Using Callbacks in X-Platform Mobile Frameworks

This async with callback setup is pretty common now thanks to the popularity of Node.js, which has made what they call non-blocking i/o a core part of their architecture.

In a cross platform framework, such as Titanium, we usually see asynchronous requests when making HttpClient calls to a server, then waiting to hear back a response. Same with certain calls to sensors, like GPS. That stuff can take some time, so Titanium handles it asynchronously.

Bunch of callbacks, bunch of problems

What happens when we have several asynchronous calls to make, one or more of them may be dependent upon the other?

Callback Pyramids

step1(function (value1) {
  step2(value1, function(value2) {
    step3(value2, function(value3) {
      step4(value3, function(value4) {
        // Do something with value4
      });
    });
  });
});

//https://github.com/kriskowal/q

Named so because the more more steps we need to make, the code begins to look like pyramid on its end. Can get pretty messy.

Errors?

And what about error handling? if step1() above is a HttpClient request, we need to know if we get a 401 unauthorized error and maybe do something unique with it in step4 or elsewhere.

Now we’d either have to check for an error response on each step, or pass along one or more error callback functions as a second argument.

Using Promises

Promises aren’t a fixall solution, we know there is no such thing in software development. They do help with 2 of the main traps with async.

Flattening the callbacks

Taking our pyramid above and using promises, we get something like

step1()
.then(step2)
.then(step3)
.then(step4);

Inside the first step is an example of creating a Promise to use with a HttpClient request.

//step one is where the promise starts
function step1(){
    var promise = new Promise(function(resolve, reject){
    var xhr = Ti.Network.createHttpClient({
      onload: resolve,
      onerror: reject
    });

    xhr.open('GET', 'http://example.com/api/endpoint');
    xhr.send();
  });

  return promise;
}

In step2 and beyond, we could be just modifying the value and returning it

function step2(response){
  return response + 1;
}

Or even doing another async request, in which case we return a promise, like with step1.

function step3(response){
  return new Promise(function(resolve, reject){
    //async goes here

    //we can then resolve the promise
    if(/*everything is good*/){
      resolve('our new value');
    } else {
      //or reject it
      reject('error code');
    }
  });
}

In each case, we are returning a value or promise.

Error Handling

With callbacks, errors can easily get gobbled up and not noticed. (Heads up! This can happen with promises, too!). We’d either have to check for errors at each step, and/or pass an additional error handling callback.

OK, confession time – this is where promises are a still a little hazy for me. So here’s what I know:

With promises, we have several different options. The two I’ve been using are either passing both a ‘success’ and ‘failure’ function in .then() or using catch().

step1()
.then(successFunction, errorFunction);

This is good for short promise chains, but what if we have multiple functions to link together, or what if we want to catch any potential errors in successFunction? We can use .catch()

step1()
.then(step2)
.then(step3)
.catch(errorFunction);

This could be considered similar in theory to a try/catch block

try {
  something1();
  something2();
  something3();
} catch(e) {
  errorFunction(e);
}

Not Catching Errors

Just like with normal errors, errors in promises will still be thrown, however our current promise library displays in them in the console, rather than let the dev simulator/device display them in the ‘red screen of death’ on iOS, or regular error alert dialog on Android.

This is bad in a way, in that errors don’t blow up in our faces, therefore we must keep an eye on console.

What’s Next

Read more on Promises here.

Learn how to use promises in Titanium Apps.