AJAX presents a fantastic way to make the applications responsive, especially when there multiple interactive elements on todays websites, or better named
as "Single Page Applications". These applications contain their logic behind a single main page (some helper child pages or controls apart).
All the interaction is focused within the single page. Take GMail or FaceBook for example.
Such pages are highly dependent upon immediate reaction whenever a backend process is completed, and updating several areas in the user interface as a result
of a single interaction. Now that's simple, anyone can say. Have couple of functions to update the UI element's state, and call them in the success handler of the AJAX call.
1: $.ajax({
2: type: "POST",
3: url: webmethod,
4: data: param,
5: contentType: "application/json; charset=utf-8",
6: dataType: "json",
7: success: function (msg) {
8: Function1(); Function2();
9: },
10: error: function (err) {
11: alert("Error: " + err.responseText);
12: }
13: });
Now we have been doing this way for long, and its been working for us. So what's new or different we can (or should) do? The answer is, although this works fine, nothing wrong in technical.
However, the design presents a tight coupling. All applications need to have a clear "Separation of Concerns", wherein each portion does only its own responsibility.
In this example, however, we have tied up the jQuery's AJAX function call to its result functionality. Ideally, the function call should be just a boilerplate code,
where the user can pass the webmethod name and the parameter list, and leave the rest to implementation.
1: function AjaxWrapper(webMethodName, parameterListInJsonFormat) {
2: $.ajax({
3: url: "Service.svc/" + webMethodName,
4: contentType: "application/json; charset=utf-8",
5: type: "POST",
6: input: parameterListInJsonFormat
7: });
8:
9: }
This wrapper will work for all calls, the caller just needs to tell which method and what input are to be given.
What this wrapper lacks is a way to put success and failure handlers. Right now, this is a pure one-way call, with no care given to the result!
Now within jQuery, we have the concept of "promise" which fulfills this requirement automatically. "Promise" is a concept which guarantees
that the attached handler will get called upon the configured state change.
jQuery has a "$.Deferred" object, which bounds to an async call's result. The "deferred" concept is literal, the timing of processing
of an async call is non-deterministic; it is postponed (or deferred) to future.
jQuery returns a promise from its $.ajax( { } ); function. The promise object can be used to bind handlers to various events which can occur when doing
an asynchronous call. The 'promise' is that the handler will always get called once the call completes with the given event.
When a promise is first created by returning from an AJAX call, it is in a "pending" state. This means that the call has been made, however the service
is still performing its task. Once the service has completed, the promise resolves and the task is done.
In case of any exceptions in service, the promise is rejected with the task getting failed.
Whatever happens, something always happens to the promise.
Here, "done", "fail" and "always" are the events for the promise, and handlers are attached to them.
Let's illustrate with an example. We create simple and steady and historical Hello World service, where one method each return
"Hello" and other "World", just to simulate two separate but dependent tasks. We also create one method which throws
an explicit exception, to illustrate service failure. (Although the code is in C#.NET, the same service can be written in any language of choice)
1: [WebMethod]
2: public static string ReturnHello()
3: {
4:
5: Thread.Sleep(5000);
6:
7: return "Hello";
8: }
9:
10: [WebMethod]
11: public static string ReturnWorld()
12: {
13: Thread.Sleep(10000);
14: return "World";
15: }
16:
17: [WebMethod]
18: public static string ReturnException()
19: {
20: throw new Exception("There is some error");
21: }
The methods have a call to Thread.Sleep
to simulate a long running web method (otherwise the method will return immediately without giving the feel
of needing any async behaviour) :)
Now for the jQuery part. For this, we will call these webmethods through our AJAX wrapper (a bit modified) and attach handlers to the various events offered
by promise. Now since the events themselves return a promise, these can be chained to each other like other jQuery constructs.
function AjaxWrapper(webMethodName, parameterListInJsonFormat) {
var promiseObject = $.ajax({
url: "Test.aspx/" + webMethodName,
contentType: "application/json; charset=utf-8",
type: "POST",
input: parameterListInJsonFormat
});
return promiseObject;
}
var helloPromise = AjaxWrapper("ReturnHello", {});
var worldPromise = AjaxWrapper("ReturnWorld", {});
helloPromise.done(function (data) {
alert("Individual task 1 :" + data.d);
});
worldPromise.done(function (data) {
alert("Individual task 2:" + data.d);
});
helloPromise.done(function () {
alert("Too many entities want to say 'Hello'");
});
var errorThrowPromise = AjaxWrapper("ReturnException", {});
errorThrowPromise.error(function (errorObj) {
alert("This error was thrown " + errorObj.responseText);
});
errorThrowPromise.always(function () {
alert("Although the webservice call gave an error,
this will always run, just like the 'finally' clause");
});
Following is the result of running this script
Comes after 5 seconds (thread wait in method 1)
Comes immediate after above, in fact at same time but due to explicit click required for 'Ok' button
Comes after 10 sec (wait here for 2nd method call)
Comes as a result of exception thrown in the 3rd web method. Note the entire exception details are given,
including the stack trace, since I just outputed the responseText. This needs to be filtered to provide a proper handling on client side
This is a result of "always" call, illustrating something to happen regardless of service call's status
Also, we can combine multiple promises together, to perform a work which requires information from multiple web service calls. The script below will combine
the Hello and World service calls to show a joined "Hello World" :)
var combinedPromise = $.when(helloPromise, worldPromise);
combinedPromise.done(function (firstResult, secondResult) {
alert("Combined result :" + firstResult[0].d + " " + secondResult[0].d);
});
This shows the following output
That's a small introduction to this powerful feature. If you really want to explore more, have a look at some of the articles below which I read to get good ideas.
Read more here: