Dojo Deferreds and Promises

前端之家收集整理的这篇文章主要介绍了Dojo Deferreds and Promises前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

原文: http://dojotoolkit.org/documentation/tutorials/1.10/promises/index.html

版本: Dojo 1.10


Deferreds是一个神奇且功能强大的东西,是一个更伟大的东西Promises的实现。这里,我们将会学习它们的概念,以及其它一些在统一方式下同时使用promises和常规值Dojo's API。

学习这节的基础是要先学习dojo/request和dojo/Deferred知识,和这些接口的基础概念,我们将会介绍一个更加抽象的概念:promises。一个promise是一个对象,它表示一个操作结束后的最终返回值。dojo/promise接口在1.8版本后进行了重大的更新和改良。一个promise有如下特性:

> 可以在这三种状态之一的状态:unfulfilled,resolve,rejected

> 只可以从unfulfilled转变到resolved或unfulfilled转变到rejected状态

> 实现是一个then方法,用来注册提醒状态改变的回调函数

> 回调函数不能改变promise返回的修士

> 一个promise的then方法返回一个新的promise,用来在提供链式结构同时保持初始promise的值不变。

有了这些知识,我们来探究Dojo是如何实现promises的。


Defferred as a Promise

如果你觉得一个promise听起来更像是一个Deferred的话,说明你已经注意到这点了。事实上,dojo/Deferred模块是Dojo的promise接口的主要实现。如下例子:

  1. require(['dojo/request'],function(request) {
  2. // original is a Deferred
  3. var original = request.get("users-mangled.json",{
  4. handleAs: "json"
  5. });
  6. });
正如之前所说,request.get(和其它Dojo的Ajax帮助函数)返回了一个promise,这个promise就表示从server端请求结束时返回的最终值。最初,它将会是一个unfulfilled状态,然后根据server返回的结果转变为resolved或rejected状态。

我们可以在请求返回的结果promise上通过then方法注册回调函数,但是,我们只能知道then方法的返回值有一个then方法,而并不能涵盖then方法返回值的全部。你可能认为它返回了初始promise,但是它实际上返回了一个实现promise接口的简单的对象。两个最常用的方法是then和cancel。下面是一个例子:

  1. require(['dojo/_base/array','dojo/dom','dojo/dom-construct','dojo/json'],function(arrayUtil,dom,domConstruct,JSON) {
  2. // result is a new promise that produces a new value
  3. var result = original.then(function(response) {
  4. var userlist = dom.byId("userlist1");
  5.  
  6. return arrayUtil.map(response,function(user) {
  7. domConstruct.create("li",{
  8. innerHTML: JSON.stringify(user)
  9. },userlist);
  10.  
  11. return {
  12. id: user[0],username: user[1],name: user[2]
  13. };
  14. });
  15. });
  16. });
这个then调用返回了一个promise对象,这个promise对象的值将会被回调函数的返回值设定。我们能够看出,新的promise的值和最初的Deferred是不同的。
  1. // chaining to the result promise rather than the original deferred to get our new value
  2.  
  3. result.then(function(objs) {
  4. var userlist = dom.byId("userlist2");
  5. arrayUtil.forEach(objs,function(user) {
  6. domConstruct.create("li",{
  7. innerHTML: JSON.stringify(user)
  8. },userlist);
  9. });
  10. });
promise返回的值都是其回调函数的返回值,如果promise的回调函数并没有返回值,那这个promise的值将会是undefined。如果在你的chaining中出现了undefined,请确保你的promise的回调函数中提供了返回值。如果你不需要考虑chaining,那就不需要担心是否提供了返回值。

此时,我们可以检查最初的Deferred的值并没有改变:

  1. // creating a list to show that the original deferred's value was untouched
  2.  
  3. original.then(function(response) {
  4. var userlist = dom.byId("userlist3");
  5. arrayUtil.forEach(response,userlist);
  6. });
  7. });
正如我们之前所见,chaining是非常强大的,它更强大的地方在于chain中的每个对象是不可变的。

需要注意的是,Deferred实例包括另外一个属性:promise,这是一个只实现了promise接口的对象,但是代表了Deferred的返回值。该promise属性允许你最小化使用者调用你的接口所带来的负面影响,通过避免有意或无意调用resolve或reject方法,但是仍然允许他们获取最初Deferred的值。

dojo/when

dojo/when是Dojo提供的一个强大的函数,它允许你用一致的接口处理promises或常规值。dojo/when函数具有4个参数:一个promise或常规值,一个可选回调函数,一个可选错误处理函数,和一个可选过程(progress)函数。它执行以下两种情况之一:

> 如果第一个参数不是一个promise,且提供了回调函数,第一个参数的值将传递给这个回调函数并立即执行,并返回回调函数的执行结果。如果没有提供回调函数,那第一个参数值将被立即返回。

> 如果第一个参数是一个promise,在promise的then方法中提供了回调函数错误处理函数和过程(progress)函数,将会返回一个新的promise。设定回调函数用来在promise完成时执行。

下面是一个例子:

  1. function getUserList() {
  2. return request.get("users-mangled.json",{
  3. handleAs: "json"
  4. }).then(function(response) {
  5. return arrayUtil.map(response,function(user) {
  6. return {
  7. id: user[0],name: user[2]
  8. };
  9. });
  10. });
  11. }
假设用户列表不会经常改变,且可以在client端缓存而不是每次函数调用时都去获取它们。在这个情形中,因为dojo/when需要一个常规值或一个promise,getUserList可以改为返回一个promise或者一个用户的数组,然后,我们就可以用dojo/when来处理这个返回值:
  1. require(['dojo/_base/array','dojo/when','dojo/request',when,request,JSON) {
  2. var getUserList = (function() {
  3. var users;
  4. return function() {
  5. if(!users) {
  6. return request.get("users-mangled.json",{
  7. handleAs: "json"
  8. }).then(function(response) {
  9. // Save the resulting array into the users variable
  10. users = arrayUtil.map(response,function(user) {
  11. return {
  12. id: user[0],name: user[2]
  13. };
  14. });
  15.  
  16. // Make sure to return users here,for valid chaining
  17. return users;
  18. });
  19. }
  20. return users;
  21. };
  22. })();
  23. });
  24.  
  25. when(getUserList(),function(users) {
  26. // This callback will be run after the request completes
  27. var userlist = dom.byId("userlist1");
  28. arrayUtil.forEach(users,userlist);
  29. });
  30.  
  31. when(getUserList(),function(user) {
  32. // This callback will run right away since it's already in cache
  33. var userlist = dom.byId("userlist2");
  34. arrayUtil.forEach(users,userlist);
  35. });
  36. });
  37. });
也可以是你负责用来创建用户列表的API,并想为你的开发者提供一个清析的API来从server端或一个数组传递给你一个用户列表。这种情况下,你可能会用如下的一个方法
  1. function createUserList(node,users){
  2. var nodeRef = dom.byId(node);
  3.  
  4. return when(
  5. users,function(users){
  6. arrayUtil.forEach(users,function(user){
  7. domConstruct.create("li",{
  8. innerHTML: JSON.stringify(user)
  9. },nodeRef);
  10. });
  11. },function(error){
  12. domConstruct.create("li",{
  13. innerHTML: "Error: " + error
  14. },nodeRef);
  15. }
  16. );
  17. }
  18.  
  19. var users = request.get("users-mangled.json",{
  20. handleAs: "json"
  21. }).then(function(response){
  22. return arrayUtil.map(response,function(user){
  23. return {
  24. id: user[0],name: user[2]
  25. };
  26. });
  27. });
  28.  
  29. createUserList("userlist1",users);
  30. createUserList("userlist2",[{ id: 100,username: "username100",name: "User 100" }]);
如上,dojo/when允许开发者用一个接口同时处理同步和异步情形,同时在生产者和消费者范围。

用dojo/promise/all处理promises列表

dojo/promise/all代替了dojo/DeferredList模块,通过结合多个promises结果为一个promise提供了处理多个异步操作机制。有时,你需要并行获取多个源的数据,并想在所有请求都结束时获得一个通知,dojo/promise/all就提供了这样的解决方法

dojo/promise/all的使用很简单,只要向它的构造函数传递一个对象或Deferreds的数组,返回结果是一个使用在传递参数中相同键的对象,或者一个和传入数组相同顺序的数组,下面是一个说明例子:

  1. require(["dojo/promise/all","dojo/Deferred","dojo/request","dojo/_base/array","dojo/dom-construct","dojo/dom","dojo/json","dojo/domReady!"],function(all,Deferred,arrayUtil,JSON){
  2. var usersDef = request.get("users.json",{
  3. handleAs: "json"
  4. }).then(function(response){
  5. var users = {};
  6.  
  7. arrayUtil.forEach(response,function(user){
  8. users[user.id] = user;
  9. });
  10.  
  11. return users;
  12. });
  13.  
  14. var statusesDef = request.get("statuses.json",{
  15. handleAs: "json"
  16. });
  17. all([usersDef,statusesDef]).then(function(results){
  18. var users = results[0],statuses = results[1],statuslist = dom.byId("statuslist");
  19.  
  20. if(!results[0] || !results[1]){
  21. domConstruct.create("li",{
  22. innerHTML: "An error occurred"
  23. },statuslist);
  24. return;
  25. }
  26. arrayUtil.forEach(statuses,function(status){
  27. var user = users[status.userId];
  28. domConstruct.create("li",{
  29. id: status.id,innerHTML: user.name + ' said,"' + status.status + '"'
  30. },statuslist);
  31. });
  32. });
  33. });
这里,我们想从server端同时获取用户列表和状态列表,通过注册一个回调函数返回用户ID的hash,将两个Deferreds都传递给dojo/promise/all,并为其注册了一个回调函数,该回调函数检查错误,如果没有错误,就会遍历状态数组,将其和用户匹配起来。无论哪个请求先执行结束,dojo/promise/all都会返回一个Deferreds传入时的顺序的数组。

总结

Dojo的promises接口为开发者提供了两种创建更加强大的应用的机会:由于Deferred函数返回的promises是不可变的避免了一些负面影响,且dojo/when提供了一个跨越基于promise和基于常规值编码的鸿沟。基于这个,dojo/promise/all允许你用一个回调函数处理多个deferreds/promises。

猜你在找的Dojo相关文章