Implement AJAX requests with dojo: XHR, cross-domain, and other

Easily implement Ajax requests on any browser is the original intention of every Ajax framework. Dojo undoubtedly provides very rich support in this regard. In addition to XMLHttpRequest, dynamic script, iframe, and RPC also have everything, and the interface is unified and easy to use. In most cases, only one sentence can be used to achieve the goal, thus avoiding the trouble of recreating wheels. Moreover, the conceptual integrity that Dojo has always pursued is also reflected here. In other words, you will not feel any unnaturalness in the process of using Dojo’s Ajax tools. On the contrary, it is easier to have a sense of analogy, because the API model is Unified, and some of the concepts involved here (such as Deferred objects) also run through the entire Dojo.

Dojo’s XHR function

Dojo’s XMLHttpRequest function is called dojo.xhr, which seems to be the most direct besides naming itself a dollar sign Way out. It is defined in the Dojo basic library, so it can be used without additional require. It can implement any http request in the same domain. But the more commonly used ones are dojo.xhrGet and dojo.xhrPost, which are just simple encapsulation of dojo.xhr functions; of course, according to the REST style, there are also dojo.xhrPut and dojo.xhrDelete.

The parameters of these functions are all uniform. Except that the first parameter of dojo.xhr is the http method name, all dojo.xhr* series functions accept the same hash parameter, which contains the details of the request, such as url, whether to synchronize, and to be passed to the server The content (can be ordinary objects, forms, or plain text), timeout settings, the type of returned results (very rich and extensible), and callbacks when the request succeeds and fails. All dojo.xhr* functions (actually all IO functions) return the same value. They are all Deferred objects. As the name suggests, it can make things happen “delayed”, making the API more flexible to use.

The following two examples may bring a little intuitive feeling:

dojo.xhrGet({ url: "something.html", load: function( response, ioArgs){ //Do something with response console.log("xhr get success:", response); return response; //Response must be returned }, error: function(response, ioArgs){ console.log(" xhr get failed:", response); return response; //Response must be returned }});//Deferred object allows asynchronous calls to be written in synchronous call var deferredResult = dojo.xhrPost({ url: "something.html", form: formNode, //Dojo will automatically convert form to object timeout: 3000, //Dojo will ensure the validity of the timeout setting handleAs: "json" //The resulting response will be considered JSON and automatically converted to object });//When the response result is available, call the callback function deferredResult.then(function(response){ console.log("xhr get success:", response); return response; //Response must be returned}); 

First explain the timeout. Except for IE8, most XMLHttpRequest objects currently do not have a built-in timeout function, so setTimeout must be used. When there are a large number of requests at the same time, it is necessary to set a separate timer for each request, which can cause serious performance problems in some browsers (mainly IE). Dojo's approach is to use a single setInterval to poll the status of all unfinished requests at regular intervals (at an interval of 50ms), which effectively solves the timeout problem of all remote requests (including JSONP and iframe).

It is worth mentioning that there is the handleAs parameter, which can be automatically identified by setting this parameter The response content format of the server is converted into a convenient form such as an object or text. According to the documentation, it accepts the following values: text (default), json, json-comment-optional, json-comment-filtered, javascript, xml.

and it is also extensible. In fact, handleAs just tells the xhr function to call which format conversion plug-in, that is, a method in the dojo.contentHandlers object. For example, dojo.contentHandlers.json is a plug-in that handles JSON format. You can easily customize the format conversion plug-ins you need. Of course, you can also modify the behavior of existing plug-ins:

dojo.contentHandlers.json = (function(old){ return function(xhr){ var json = old( xhr); if(json.someSignalFormServer){ doSomthing(json); delete json.someSignalFormServer;} return json; }})(dojo.contentHandlers.json);//A little trick to get the original method by passing parameters 

If you want to know the details of each parameter, you can refer to Dojo’s text files.

Virtual parameter class

Here are two special features of Dojo in API design. The first is the concept of virtual parameter "class": by taking advantage of the flexible extension of the javascript object, it is mandatory to specify that a hash parameter belongs to a certain "class". For example, the parameters accepted by the dojo.xhr* series of functions are called dojo.__XhrArgs. This "class" does not exist in the actual code (don't try to use instanceof to verify it), just stay in the concept, more abstract than the abstract class, so prefix it with a double underscore (Dojo is used to add a single underscore prefix to abstract classes) ). This may seem trivial, but it actually simplifies the API, because it creates a connection between the APIs, which is easier to remember and easier to use. This is more obvious when "inheriting" such classes. For example, dojo.__XhrArgs inherits from dojo.__IoArgs, which is the set of parameters that all IO functions must support. Also inherited from dojo.__IoArgs are dojo.io.script.__ioArgs and dojo.io.iframe.__ioArgs, which are used respectively Dynamic script requests and iframe requests. The subclass only adds a small number of attributes to the parent class, so that a large number of parameters have a tree-like structure. The original hash parameter is to replace the fixed parameter order with precise parameter names. While increasing flexibility and scalability, it actually increases the amount of memory (after all, the parameter names cannot be misspelled), making the API look nothing like It is so easy to use, and the design of the parameter class alleviates this problem.

This kind of parameter class approach can be seen everywhere in Dojo. If you read the source code, you will find that they are all declared in a special comment format in the form of normal code. Like this:

/*=====dojo.declare("dojo.__XhrArgs", dojo.__IoArgs, {constructor: function(){ //summary: //... //handleAs: //.. . //...} }); =====*/ 

This format can be automatically extracted into a document by the jsDoc tool. In the document, these virtual classes are as complete as real classes.

Deferred Object

Another API design feature is the extensive use of Deferred objects. The Deferred in Dojo is based on the MochiKit implementation with a slight improvement, while the latter is inspired by the concept of the same name in the Python event-driven network toolkit Twisted. In a nutshell, the role of this object is to separate the declaration position of the callback function in the asynchronous IO from the calling position, so that when an asynchronous IO is finally completed, the developer can simply say "The goods have arrived, and you can come Take it" without specifying which callback function should be called. The advantage of this is that asynchronous IO is written in the same way as synchronous IO (data processing is always outside of the data function, not inside), thereby simplifying asynchronous programming.

The specific approach is that asynchronous functions always return a proxy object synchronously (this is the Deferred object), which can be regarded as a representative of the data you want, and it provides some methods to add callbacks Functions, when data is available, these callback functions (which can be many) will be executed in the order in which they are added. If an error occurs in the process of fetching data, the provided error handling function will be called (there can also be many); if you want to cancel this asynchronous request, you can also use the cancel method of the Deferred object to complete.

The core method of dojo.Deferred is as follows:

then(callback, errback); //add callback function callback(result); //indicating the successful completion of the asynchronous call, trigger the callback function errback(error ); //Indicates that an error occurred in the asynchronous call and triggers the error handling function cancel(); //Cancel the asynchronous call

Dojo also provides a when method to synchronize the value and The asynchronous Deferred object is written in the same way when used. For example:

//Implementation of a tool function var obj = {getItem: function(){ if(this.item){ return this.item; //Return data synchronously here}else{ return dojo.xhrGet({ //What is returned here is the Deferred object url: "toGetItem.html", load: dojo.hitch(this, function(response){ this.item = response; return response; }) });} }};//User Code dojo.when(obj.getItem(), function(item){ //No matter synchronous or asynchronous, the way to use the tool function getItem is the same});

With the help of function closure , The creation and use of Deferred objects becomes easier. You can easily write a function to create Deferred objects to do asynchronous things in a synchronous way. For example, write a function that uses store to get data:

var store = new dojo.data.QueryReadStore({...}); function getData(start, count){ var d = new dojo.Deferred(); //initialization A Deferred object store.fetch({ start: start, count: count, onComplete: function(items){ //Fetch the Deferred object in the upper closure directly d.callback(items);} }); return d; / /Return it as the result}

Cross domain with dojo.io.script

dojo.xhr* is just an encapsulation of the XmlHttpRequest object. Due to the restriction of the same-origin policy, it cannot send cross-domain requests. Cross-domain requests must be created dynamically.