In CoffeeRun, your modular code has helped you avoid the dreaded “spaghetti code” that can easily happen when you mix event-handling (UI) code with your application’s internal logic.
Your modules interact via function arguments, also known as callbacks. Callbacks are a fine solution for situations in which you have code that only depends on a single, asynchronous step. Figure 14.1 shows a simplified version of one asynchronous flow from CoffeeRun.
What happens when you have many dependent asynchronous steps? One option is to nest lots of callbacks, but this quickly becomes unwieldy and dangerous. With a simplified version of your submit handler code that does extra error checking, that approach might look like this:
formHandler.addSubmitHandler(function (data) { try { myTruck.createOrder(function (error) { if (error) { throw new Exception(error) } else { try { saveOnServer(function (error) { if (error) { throw new Exception({message: 'server error'}); } else { try { checkList.addRow(); } catch (e2) { handleDomError(e2); } } }) } catch (e) { handleServerError(e, function () { // Try adding the row again try { checkList.addRow(); } catch (e3) { handleDomError(e3); } }); } } }); } catch (e) { alert('Something bad happened.'); } });
Promise
s, which you will learn about in this chapter, are a better solution.
That same series of steps might be expressed as
a chain of Promise
s
like this:
formHandler.addSubmitHandler() .then(myTruck.createOrder) .then(saveOnServer) .catch(handleServerError) .then(checkList.addRow) .catch(handleDomError);
Promise
s provide a way to architect very complex
asynchronous code in a manageable way, and in this chapter you
will use them to simplify the architecture of CoffeeRun.
Promise
s are a relatively new feature, but they are well supported
in recent browsers, including Chrome.
In CoffeeRun, you are
mainly interested in performing the next step if the current
one succeeds without errors.
Promise
s make this simple.
Instead of relying on callback arguments,
you will return Promise
objects, which will let you decouple
your modules even further.
Promise
objects are always in one of three states:
pending, fulfilled,
or rejected (Figure 14.2).
Every Promise
object has a then method that is triggered when the Promise
becomes fulfilled.
You can call then and pass it a callback;
when the Promise
is fulfilled, the callback is invoked and
passed whatever value the Promise
received
when doing its asynchronous work.
You can also chain multiple then calls together.
Instead of writing functions that accept and then invoke
callbacks, it is better to return Promise
objects and let the caller
chain a then off of that Promise
.
You are going to start with jQuery’s Deferred
object, which works similarly
to a Promise
for simple use cases.
jQuery’s $.ajax methods (including $.post
and $.get) return a Deferred
.
Deferred
objects have methods that let you register callbacks for two of their states:
fulfilled and rejected.
You are going to start by updating RemoteDataStore so that it returns the
Deferred
s produced by jQuery’s Ajax methods. Later, you will modify
your other modules to register callbacks with the Deferred
s.
3.137.176.166