Finally, it needs a better name than "add-on" (Add-On 2.0!) and it needs
developers/sponsors, and all that fun stuff.
+2013-12-04 Promises in AngularJS
+--------------------------------
+
+One of my favorite new-ish tools in the Javascript world are called
+"Promises".
+
+****
+If a function cannot return a value or throw an exception without
+blocking, it can return a promise instead. A promise is an object that
+represents the return value or the thrown exception that the function
+may eventually provide. A promise can also be used as a proxy for a
+remote object to overcome latency. -- https://github.com/kriskowal/q
+****
+
+Typically, with asynchronous network calls, the caller will make the
+call and pass in a callback function, which is invoked when the async
+call returns. This works well enough for single calls, but it does not
+scale well. Promises allow us to manage collections of asynchronous
+calls much more elegantly.
+
+Here's a quick example:
+
+[source,js]
+-----------------------------------------------------------------------------
+// non-promise api call
+request(service, method, params, {oncomplete : callback});
+
+// promise-based call
+request(service, method, params).then(callback);
+-----------------------------------------------------------------------------
+
+At first, the difference seems trivial, but it becomes more
+pronounced as more requests are added.
+
+Batch Parallel Request Example
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[source,js]
+-----------------------------------------------------------------------------
+// non-promise batch
+// send three, parallel requests and track the
+// responses to see when all are done
+var expected = 3;
+var checkComplete = function() {
+ if (--expected == 0) { // decrement w/ each response
+ // all responses received, move on to other things
+ }
+}
+request(s, m, p, function(r) { /*do stuff*/; checkComplete() });
+request(s, m, p, function(r) { /*do stuff*/; checkComplete() });
+request(s, m, p, function(r) { /*do stuff*/; checkComplete() });
+
+// promise-based batch
+var promises = [];
+promises.push(request(s, m, p).then(function(r) {/*do stuff*/}));
+promises.push(request(s, m, p).then(function(r) {/*do stuff*/}));
+promises.push(request(s, m, p).then(function(r) {/*do stuff*/}));
+$q.all(promises).then(
+ function() {
+ // all responses received, move on to other things
+ }
+);
+
+// $q.all() takes a collection of promise objects and returns a new
+// promise which is resolved when all of the embedded promises are resolved
+-----------------------------------------------------------------------------
+
+Not only is the code a little cleaner, but it flows in the same
+direction as execution. In other words, checkComplete(), which is the
+last-executed code in the non-promise example is defined before the
+requests are sent. This creates a spaghetti effect in the code, where
+you have to jump around to follow the logic. Not so in the promise
+example. It flows top to bottom.
+
+Attaching to Promises
+~~~~~~~~~~~~~~~~~~~~~
+
+Another neat trick you can do w/ promises is create a promise to
+represent a long-running task. When clients ask for the result of the
+task and the task is not yet complete, the promise can be returned to
+any number of clients.
+
+[source,js]
+-----------------------------------------------------------------------------
+function longTask() {
+ if (this.promise) {
+ return this.promise;
+ } else {
+ this.promise = performAsyncTask();
+ return this.promise;
+ }
+}
+
+// the first call kicks off the long-running process
+longTask().then(/*func*/);
+
+// subsequent calls from other locations simply pick up the existing promise
+
+// different location in the code
+longTask().then(/*func*/);
+
+// different location in the code
+longTask().then(/*func*/);
+
+// different location in the code
+longTask().then(/*func*/);
+
+// when longTask() finally completes, all promise handlers are run
+// Also, if longTask() was *already* complete, the promise handler
+// will be immediately run.
+-----------------------------------------------------------------------------
+
+This structure is used to great effect in the prototype page
+initialization code. Independent controllers that all want to ensure
+the client has authenticated and retrieved necessary data will call the
+startup code, the first will kick it off, and the others will latch on
+the outstanding promise.
+
+As a final bonus to using promises within Angular, promise resolution
+causes another $digest() run in Angular, which causes templates to get
+updated.
+
+[source,js]
+-----------------------------------------------------------------------------
+egNet.request(
+ 'open-ils.actor',
+ 'open-ils.actor.user.retrieve',
+ egAuth.token(), userId
+).then(
+ function(user) {
+ $scope.patron = patron;
+ }
+);
+-----------------------------------------------------------------------------
+[source,html]
+-----------------------------------------------------------------------------
+<!-- username will magically display in the
+ page after the async call completes -->
+<div>Username: {{patron.usrname()}}</div>
+-----------------------------------------------------------------------------
+
+Promises also support reject() and notify() handlers for failed requests
+and intermediate messages. In my egNet module, I'm leveraging notify()
+for streaming responses.
+
+[source,js]
+-----------------------------------------------------------------------------
+egNet.request(service, method, params).then(
+ function(finalResp) { console.log('all done') },
+ function() { console.error('request failed!') },
+ function(resp) { console.log('got a response ' + resp) }
+);
+// The egNet module has additional examples and docs.
+-----------------------------------------------------------------------------
+
+For the full technical rundown, see also
+http://docs.angularjs.org/api/ng.$q[Angular $q Docs].
+
Future Topics...
----------------