When developing more complex apps against the REST API, you must often make multiple
asynchronous RESTful calls. For example, an initial RESTful call might
retrieve master data, whereas subsequest calls fill in child data. The
challenge with multiple
calls is that they need to happen sequentially, but each call is made
asynchronously. So the only solution is to nest dependent calls within
the “success” callback functions. Example 6 shows an example of nested
RESTful calls, wherein the first call retrieves the current user’s
account name and the nested call returns the social feed for the
account name.
Example 6. Nested RESTful calls
"use strict";
var Wingtip = window.Wingtip || {};
Wingtip.FeedViewModel = function () {
var init = function () {
//Get the current user's account information
$.ajax({
url: _spPageContextInfo.webServerRelativeUrl +
"/_api/SP.UserProfiles.PeopleManager/GetMyProperties",
method: "GET",
headers: {
"accept": "application/json;odata=verbose",
},
success: function (data) {
//Now get the current user's social feed
var accountName = data.d.AccountName;
$.ajax({
url: _spPageContextInfo.webServerRelativeUrl +
"/_api/social.feed/actor(item='" +
accountName +"')/Feed",
method: "GET",
headers: {
"accept": "application/json;odata=verbose",
},
success: function (data) {
var feedData = data.d;
},
error: function (err) {
alert(JSON.stringify(err));
}
}
);
},
error: function (err) {
alert(JSON.stringify(err));
}
}
);
};
return {
init: init
}
}();
The challenge with the code in Example 6 is that is can rapidly become unmaintainable. Multiple levels of nested
asynchronous calls simply creates a pile of “spaghetti” code. What is
needed is a mechanism to separate out the various asynchronous calls
while still maintaining the dependency between them. That is the
function of a promise.
A promise—also known as a deferred—is
a pattern that returns an object immediately from an asynchronous call.
This object will later be populated with the result of the asynchronous
call, but its immediate return simplifies the code structure making
it much more maintainable. Furthermore, promises provide a built-in
caching mechanism so that the same query does not have to be run again
if the promise has already been successfully fulfilled.
There are several techniques for implementing promises, but one of the easiest is to make use of the jQuery $.Deferred method. Using the $.Deferred
method, you can create a deferred object, which can be immediately
returned from an asynchronous call. The deferred object has resolve and
reject methods, which are called on success or failure respectively.
Using deferreds makes it possible for you to separate the JavaScript
code that performs the asynchronous call. As an example, Example 7 shows how to implement the pattern to get the user’s profile information.
Example 7. The promise pattern
"use strict";
var Wingtip = window.Wingtip || {};
Wingtip.ProfileQuery = function () {
var deferred = $.Deferred(),
execute = function () {
$.ajax(
{
url: _spPageContextInfo.webServerRelativeUrl +
"/_api/SP.UserProfiles.PeopleManager/GetMyProperties",
method: "GET",
headers: {
"accept": "application/json;odata=verbose",
},
success: function (data) {
deferred.resolve(data);
},
error: function (err) {
deferred.reject(err);
}
}
);
return deferred;
};
return {
execute: execute
}
}();
The deferred object exposes a “promise”
object that has a method named “then.” The “then” method takes two
arguments: the first argument is a success function, the second is a
failure function. So the library in Example 7 can be invoked easily by using the code in Example 8.
Example 4-8. Invoking the asynchronous call
Wingtip.ProfileQuery.execute().promise().then(
//success
function (data) {
var accountName = data.d.AccountName;
},
//failure
function(err) {
alert(JSON.stringify(err));
}
);
The promises pattern significantly simplifies JavaScript code when your app must make multiple, nested asynchronous calls, and that makes it very powerful. However, the promise
object also acts like a caching mechanism in that the success or
failure function will be called immediately if the promise has already
been fulfilled. This opens up additional ideas such as creating
arrays of promises that contain fulfilled data so that apps do not have
to run queries that have already successfully executed.