Author: Bryan Forbes
Translator: feijia ([email protected])
Original link: http://dojotoolkit.org/documentation/tutorials/1.6/deferreds/
Applicable dojo version: 1.6
Difficulty: Medium
< p style="margin:10px auto; font-family:verdana; font-size:14px; line-height:22.3999996185303px"> In this tutorial, you will learn the basics of using Dojo’s defer (delay) method.
Using the delay method, you can elegantly and easily handle the common asynchronous in JS programming transfer. We will also explain how to use DeferredList, which can handle multiple delayed asynchronous calls at once.
Getting Started
At first sounds, the name “Deferred” is quite mysterious. But it is actually a powerful tool provided by Dojo to handle asynchronous calls (such as Ajax). Simply put, a Deferred object will wait for a period of time before executing the specified call until a specific event occurs or before One action is completed. Ajax is a common example: After sending an Ajax request, we need to wait for the server to return the response before calling the processing function. So what the Deferred object provides us is the ability to delay an action and wait for an event to occur. In this tutorial, we will combine the content of the Ajax tutorial to interpret how to use Deferred objects to better manage asynchronous JS programs.
dojo.Deferred
The concept of delay is not exclusive to Dojo, (Translator : Jquery and other JS frameworks also have similar objects and implementations. The CommonJS organization has also defined a set of standards for this). Since version 0.3, Dojo has implemented the dojo.Deferred object.
The Deferred object has three states. It is in the “unresolve” state when it is initialized. When the waiting event occurs, it enters the “resolve” state. The third state is the error state, that is, the event does not develop as expected. For example, the server returns an error message, also called the reject state.
After the Deferred object is created, we can register a callback function by calling the then method of the object, which means that when an event that the Deferred object is waiting for occurs (resolve), the callback is called function. The then method also accepts a second parameter, which can specify an error callback function to be called when the event fails or an error occurs (reject).
Let’s look at one Example:
- vardef=newdojo.Deferred(),
- userlist = dojo.byId(“userlist”);
- def.then(function(res){
- // This will be called when the deferred
- // is resolved
- dojo. forEach(res, function(user){
- dojo.create(“li”, { span>
- id: user.id,
- innerHTML : User.username + “:”+user.name
- , userlist);
- },function(err){
- // This will be called when the deferred
- // is rejected
- dojo.create(“li”,{
- inne rHTML: “Error:”+err
- , userlist);
- });
- li>
- dojo.xhrGet({
- url:“users.json”< span style="margin:0px; padding:0px; border:none; background-color:inherit">,
- handleAs:“json”,
- load: function< span style="margin:0px; padding:0px; border:none; background-color:inherit">(res){
- // Resolve when content is received
- def.resolve(res);
- ,
- error: function(err){
- // Reject on error /span>
- def.reject(err); li>
- });
View example
In the above example, we created a dojo The .Deferred object has a success callback function and an error callback function registered on it. We also called dojo.xhrGet, an asynchronous Ajax call, to get “user.json” from the server. If this ajax call is successful, we will set the dojo.Deferred object to the resolved state in the callback function set by the load attribute of the xhr object, and then the callback function we registered on the Deferred object will be called; if ajax If the call fails, the error callback function registered on Deferred will be called.
The above example, you may think it is unnecessary, why not directly set the success and failure callback functions directly in xhrGet? Yes, you can do this, but by introducing the Defered object, we will take care of it. The logic of returning data from the server (callback function) and the logic of sending Ajax requests are used to understand the coupling.
In fact, in order to facilitate developers to use the Defered object, Dojo’s Ajax constructor method will directly return you a Deferred object, so the above code can be simplified Quite a few:
- var def= dojo. xhrGet({
- url:“users.json”,
- handleAs:“json”
- });
- def.then(function(res){ span>
- var userlist= dojo.byId(“userlist”);
- dojo.forEach (res, function (user){
- dojo.create(“li”, {
- id: user.id,
- innerHTML: user.username + “:”+user.name
- , userlist); /span>
- });
- },function(err){
- // This shouldn’t occur, but it’s defined just in case
- alert(“An error occurred: “ + err);
- });
查看示例
在这个例子中我们不再需要设置dojo.xhrGet的 load属性了,可以直接在xhrGet返回的Deferred对象上通过then来注册回调函数. 代码逻辑更加直观简洁.
在回调函数中,我们遍历了从服务器端返回的用户列表,并且为每个用户创建了一个HTML列表 。从功能上看,和前一个例子完全一样,但是在这个例子中,我们得以把处理服务器端数据的逻辑和发送Ajax请求的逻辑分开了。所以Deferred对象的一个重要功能就是对我们的程序逻辑进行解藕。 (decoupling)
链式调用
dojo.Deferred是个挺容易理解的概念,但是它还有一些很强大的功能值得我们继续探索. 其中之一就是链式调用(Chaining):每个then方法的返回值都仍然是一个Defered对象。我们来看一个例子:
假设前面的例子里服务器端返回的不是JSON格式的用户对象,而是每个用户的信息的原始值。值当然不如对象方便使用,所以我们希望注册一个回调函数来把这些原始数值转换为用户对象。
- var original = dojo.xhrGet({
- url: “users-mangled.json”,
- handleAs: “json”
- });
- var result = original.then(function(res){
- var userlist = dojo.byId(“userlist1”);
- return dojo.map(res, function(user){
- dojo.create(“li”, {
- innerHTML: dojo.toJson(user)
- }, userlist);
- return {
- id: user[0],
- username: user[1],
- name: user[2]
- };
- });
- });
- // 由original的then方法返回的result对象也有一个`then` 方法来接受回调函数,和original对象一样。
- // 但是要注意的是传给result.then中注册的回调函数的值,不是Ajax调用获取的数据, 而是original的回调函数的返回值。
- // 也就是已经经过格式转换的用户对象map
- result.then(function(objs){
- var userlist = dojo.byId(“userlist2”);
- dojo.forEach(objs, f unction(user){
- dojo.create(“li”, {
- innerHTML: dojo.toJson(user)
- }, userlist);
- });
- });
注意: 严格来说then方法的返回值并不是一个Deferred对象,它有个特定的名字”promise”, 即承诺,实现了一个特定的API. 你可以进一步阅读关于prommise的教程 来深入学习, 不过在这里,我们可以暂时理解为 一个promise对象提供了和Deferred对象完全相同的then方法。 因此Deferred对象和Promise对象的then方法可以进行连续的链式调用。这样做的好处是什么呢?链式调用时,原始的Defered对象不会被修改,而且服务器端的返回的数据也没有被修改,你可以继续在original的defered对象上注册其他的回调函数来对原始数据进行进一步操作,在前面的例子基础上,你可以注册一个新的回调到original上,例如:
- original.then(function(res){
- var userlist = dojo.byId(“userlist3”);
- dojo.forEach(res, function
(user){ - dojo.create(“li”, {
- innerHTML: dojo.toJson(user)
- }, userlist);
- });
- });
- var def = new dojo.Deferred(),
- userlist = dojo.byId(“userlist”);
- def.then(function(res){
- // This will be called when the deferred
- // is resolved
- dojo.forEach(res, function(user){
- dojo.create(“li”< span style="margin:0px; padding:0px; border:none; background-color:inherit">, {
- id: user.id,
- innerHTML: user.username + “: “ + user.name
- }, userlist);
- });
- },function(err){
- // This will be called when the deferred
- dojo.create(“li”, {
- innerHTML: “Error: “ + err
- }, userlist);
- });
- dojo.xhrGet({
- url: “users.json”,
- handleAs: “json”,
- load: function(res){
- // Resolve when content is received
- def.resolve(res);
- },
- error: function(err){
- // Reject on error
- def.reject(err);
- }
- });
- var def = dojo.xhrGet({
- url: “users.json”,
- handleAs: “json”
- });
- def.then(function(res){
- var userlist = dojo.byId(“userlist”);
- dojo.forEach(res, function(user){
- dojo.create(“li”, {
- id: user.id,
- innerHTML: user.username + “: “ + user.name
- }, userlist);
- });
- },function(err){
- // This shouldn’t occur, but it’s defined just in case
- alert(“An error occurred: “ + err);
- });
- var original = dojo.xhrGet({
- url: “users-mangled.json”,
- handleAs: “json”
- });
- var result = original.then(function(res){
- var userlist = dojo.byId(“userlist1”);
- return dojo.map(res, function(user){
- dojo.create( “li”, {
- innerHTML: dojo.toJson(user)
- }, userlist);
- return {
- id: user[0],
- username: user[1],
- name: user[ 2]
- };
- });
- < span style="margin:0px; padding:0px; border:none; color:rgb(0,0,0); background-color:inherit"> });
- // 由original的then方法返回的result对象也有一个`then` 方法来接受回调函数,和original对象一样。
- // 但是要注意的是传给result.then中注册的回调函数的值,不是Ajax调用获取的数据, 而是original的回调函数的返回值。
- // 也就是已经经过格式转换的用户对象map
- result.then(function(objs){
- var userlist = dojo.byId(“userlist2”);
- dojo.forEach(objs, functi on(user){
- dojo.create(“li”, {
- innerHTML: dojo.toJson(user)
- }, userlist);
- });
- });
- original.then(function(res){
- var userlist = dojo.byId(“userlist3”);
- dojo.forEach(res,
function(user){ - dojo.create(“li”, {
- innerHTML: dojo.toJson(user)
- }, userlist);
- });
- });