Asynchronous XMLHttpRequests with XhrIo

Overview

JavaScript's XMLHttpRequest enables the responsive,
persistent user interfaces that characterize AJAX applications. Web
applications can use XMLHttpRequest to communicate with
the server from JavaScript without reloading the page or blocking.

But while most modern browsers support XMLHttpRequest,
different browsers have different implementations. To make sure your
app has the same behavior for each browser, you have to do extra work.

You can smooth over these browser differences by using the Closure
Library's XhrIo class. XhrIo also adds the
ability to set timeouts, and handles all notification for
both readyState changes and timeouts with a single set of
informative event types.

This document first introduces XhrIo with a quick example, then
describes in greater detail the process of sending a query,
monitoring its status, and processing its results.

Quick Examples

There are two ways to send an asynchronous request
with XhrIo: by calling the
the goog.net.XhrIo.send() utility function, and by
instantiating goog.net.XhrIo and calling the
instance's send() method.

send() Utility Function Example

The Closure Library provides a simple utility function for the
common use case of one-off requests. The following example
illustrates its use.

A function to call when the response is
received. In the context of this callback function, the keyword
this refers to an XhrIo
instance holding the result. We use the getResponseJson()
method of this instance to evaluate the response as JSON and
retrieve the resulting JavaScript object.

When the document loads, getData() sends an asynchronous
network request for
xhrio_data.json. Note that in this simple example the
response is just a static file served by the web server; in a real web
application the server might generate this response
dynamically. In this case, the file we're requesting looks like this:

{
"title": "My JSON Data",
"content": "What's mine is yours."
}

This text consists of proper JavaScript syntax for an object literal
(i.e., JavaScript Object Notation or JSON), so we retrieve the
response as a JavaScript object using the
getResponseJson() method. You can also retrieve and parse
XML, or you can simply get the raw text of the response, as described
in Retrieving Request Results.

send() Instance Method Example

You can also send a request by creating a new XhrIo instance, adding an event listener,
and calling the send() method on the instance:

Sending and Monitoring a Request Using XhrIo

The examples above illustrate the two ways to send a request
with XhrIo: the send() utility function and
the send() instance method. These two approaches have
advantages and disadvantages:

The send() utility function

Pros: simple

Cons: limited, inefficient

The send() instance method

Pros: flexible, allows reuse of XhrIo
instances

Cons: takes more steps to set up

The following sections discuss each of these two approaches in greater detail.

Using XhrIo's send() Utility Function

The utility function XhrIo.send() provides a way to make an
asynchronous request with a single call. It takes an optional callback
parameter, and this callback is called once whenever
the request has been resolved or has been determined to have failed for
any reason (HTTP error, timeout, etc.). The complete signature
for XhrIo.send() is:

The URL to which you want to
send the HTTP request. This can be a string or an instance
of goog.Uri.

opt_callback

The function to be called when
the request has been completed. The callback is passed an event object
representing the request completion, and the target
property of this event object points to the XhrIo object
used to send the request.

opt_method

The HTTP method of the
request (usually either "POST" or "GET"). Defaults to "GET".

opt_content

The data to be posted
for POST requests.

opt_headers

A map of key-value pairs to be
added to the HTTP request as headers. Either a plain
JavaScript Object or
a goog.structs.Map.

opt_timeoutInterval

The number of
milliseconds after which an incomplete request will be aborted. A
value of 0 prevents a timeout from being set, in which case the request
is governed by the browser's timeout (which is usually 30
seconds).

XhrIo.send() sends its request by creating
an XhrIo instance. It attaches an event listener to
the XhrIo to listen for request completion. It also
attaches a listener that automatically disposes of
the XhrIo after the completion message has been
dispatched. Note that XhrIo.send() creates a
new XhrIo instance for every request.

When the request completes, the callback function is passed an event
object representing the completion. The target property of
this object refers to the XhrIo instance used to execute
the request. You can use this target property to retrieve
information from the XhrIo instance.

For example, if your callback parameter is called e, in
your callback function you can
call e.target.isSuccess(), e.target.getResponseText(),
or any of the other instance methods of XhrIo. When you
use the static send() method you don't have the
opportunity to attach your own event handlers to its
short-lived XhrIo instance. The opt_callback
parameter is the only vehicle for notification available to you, and
it is only called for 'complete' events (which will be fired for both
successfully completed and aborted requests).

Using XhrIo's send() Instance Method

XhrIo's send() instance method provides a
wider and more flexible interface than the utility function. Instead
of a single callback function, this approach uses event listeners to
get information about the status of the request.

To send a request using the send() instance
method:

Create or acquire an XhrIo instance.

Attach event listeners to the instance to monitor the request and
process its results.

Optionally set a timeout interval.

Send requests by calling the instance's send()
method.

Creating or Acquiring an XhrIo
Instance

The constructor XhrIo() does not take any parameters. You
define a request by passing parameters to the
instance's send() method rather than through constructor
parameters.

Using goog.net.XhrIoPool

If you need to submit multiple requests simultaneously, consider using
an instance
of goog.net.XhrIoPool. XhrIoPool maintains a pool
of XhrIo instances, supplying an instance on request if
one is available (that is, if there is an instance in the pool that is
not currently processing a request).

To complete signature of the XhrIoPool constructor is:

new goog.net.XhrIoPool(opt_headers, opt_minCount, opt_maxCount)

opt_headers

A map of HTTP header name/value
pairs to add to every request sent with
an XhrIo instance from the
pool.

opt_minCount

The minimum number
of XhrIo instances to keep in the pool
at all times (defaults to 1).

opt_maxCount

The
maximum number of XhrIo instances
(defaults to 10).

To get an XhrIo instance from the pool, call

getObject(opt_callback, opt_priority)

where opt_callback is a callback function that is called
when an XhrIo instance becomes available (with the
instance passed as the parameter to the callback),
and opt_priority is a number that determines which
requests get fulfilled first if there is a shortage of instances in
the pool (lower numbers indicating higher priority). If you
omit opt_callback, an instance is returned if one is
available, and undefined is returned if no instance is
available.

When you are done using an XhrIo instance from a pool,
call the pool's releaseObject(obj) method
(where obj is the XhrIo instance) to return
the instance to the pool so that it can be reused.

In addition to the queuing facilities provided
by XhrIoPool, the Closure Library also
provides more sophisticated connection management
through goog.net.XhrManager.
XhrManager uses an XhrIoPool instance, but
enhances the basic pool with automatic retries and aggregate event
handling. Its use is beyond the scope of this document, but you might
find it helpful if you need advanced connection management
capabilities.

Handling Request Events

Listening for Request Events

Attach event listeners to the XhrIo instance to monitor
request status and process request results. XhrIo listens
for the native readystatechange events, performs the
required lookup and interpretation of the readyState, and
then dispatches its own events.
XhrIo dispatches the following types of events:

Event type constant

When event is dispatched

goog.net.EventType.READY_STATE_CHANGE

Dispatched on any change to the ready
state. In other words, fired in response to any native
browser readystatechange event.

goog.net.EventType.COMPLETE

Dispatched when the request has been resolved, whether successfully or
not.

goog.net.EventType.SUCCESS

Dispatched after the COMPLETE event fires if the request
completed successfully.

goog.net.EventType.ERROR

Dispatched after the COMPLETE event is dispatched if
the request did not complete successfully.

goog.net.EventType.READY

Dispatched
after the XhrIo instance has been reset
following the completion of the previous request. Indicates that
the XhrIo instance is ready for another
request.

goog.net.EventType.TIMEOUT

Dispatched if the request does not complete before the timeout
interval has elapsed (see Setting a Timeout
Interval). After the TIMEOUT event is dispatched, the
request is aborted, triggering the dispatch of a COMPLETE
event and then an ABORT event

goog.net.EventType.ABORT

Dispatched by the abort method
of XhrIo. Before dispatching
an ABORT event, the abort method:

calls abort() on
the XhrIo
instance's XMLHttpRequest,

records an error code in the XhrIo
instance, and

dispatches a COMPLETE event.

In the quick example, for example, an event handler is attached to the XhrIo instance xhr with the following code:

Checking the Request's Status

XhrIo provides several methods for checking the status of
a request and getting its results. You may find these methods useful
in your event listener functions:

Method

Returns

isActive()

true
if send() has been called on the XhrIo
instance and the request triggered by this call has not yet errored,
been aborted, or completed successfully.

isComplete()

true if the
underlying XMLHttpRequest has
a readyState of 4.

isSuccess()

true if the request
has completed without an error or timeout.

getReadyState()

The readyState
of the underlying XMLHttpRequest.

getStatus()

The HTTP status code sent by the
server (200 for success, 404 if the URL wasn't found, etc.) if the
server has started sending its response. If the server has not started
responding, returns -1.

getStatusText()

The status text
sent by the server ("OK", "Not found", etc.) if the server has started sending
its response. If the server has not started responding, returns an
empty string.

getLastUri

The last Uri that was
requested.

getLastErrorCode()

A goog.net.ErrorCode value indicating
the outcome of the latest
request. goog.net.ErrorCode (defined
in closure/net/errorcode.js) defines constants used to
record network error types, including the following types used
by XhrIo:

goog.net.ErrorCode.NO_ERROR

goog.net.ErrorCode.EXCEPTION

goog.net.ErrorCode.HTTP_ERROR

goog.net.ErrorCode.ABORT

goog.net.ErrorCode.TIMEOUT

getLastError()

Text indicating the source of
the error produced by the latest request, or an empty string if there
was no error.

getResponseHeader(key)

The value of the HTTP
response header indicated by key.

Retrieving Request Results

XhrIo provides three methods for retrieving the results
of a request. If you need to get information back from the server,
then you will have to call one of these methods. These methods differ
from each other only with respect to the way in which the response
text is processed and returned: as raw text, as a JavaScript object
obtained by evaluating a JavaScript Object Notation (JSON) response,
or as Document object obtained by parsing an XML response.

Method

Returns

getResponseText()

The raw text of the
response. The server's response can always be accessed with this
method, no matter how it is encoded.

getResponseJson()

Evaluates the response
as JavaScript and returns the resulting object. The server's response
must be JSON-encoded in order for this method to work. That is, the
response must be a JavaScript object expression. Note
that getResponseJson() uses a safe JSON parse
(goog.json.parse()),
and might therefore be slow for large amounts of data. If you trust
the data you can instead retrieve the raw text response
with getResponseText() and pass it
to goog.json.unsafeParse() to get a JavaScript
object.

getResponseXml()

Returns
a Document object produced by parsing
the response text as XML. The server's response must have a content
type of "text/xml" in order for this to
work. The getResponseXml() method returns the value of
the responseXML property of the
underlying XMLHttpResponse object.

Setting a Timeout Interval

The timeout interval is a property of the XhrIo
instance itself, and persists across requests. Set the timeout using
the goog.net.XhrIo.prototype.setTimeoutInterval() method,
which takes a parameter indicating the number of milliseconds to wait
before aborting a request. XhrIo interprets a timeout of
0 milliseconds as indicating no timeout at all. If you do not set a
timeout for the XhrIo instance (or if you set a timeout
of 0), requests default to the browser's timeout, which is
usually 30 seconds.

Sending the Request

Once you have created an XhrIo instance
and attached event listeners to handle the request results, initiate the
request by calling the instance's send() method:

send(url, opt_method, opt_content, opt_headers)

url

indicates the uri to which you want to send the
HTTP request.

opt_method

indicates the HTTP method of the request
(usually either "POST" or "GET"). Defaults to "GET".

opt_content

contains the data to be posted for POST
requests.

opt_headers

contains key-value pairs to be added to the
HTTP request as headers. Either a plain
JavaScript Object or
a goog.structs.Map.

As discussed above, the send() method does not take a
parameter indicating the timeout period to use for the request. By
default an XhrIo instance has no timeout, but if you want
to set a timeout call setTimeoutInterval(interval) before
calling send(), where interval is the timeout
interval in milliseconds.