Dojo Deferreds and Promises

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

Version: Dojo 1.10


Deferreds is a magical and powerful thing. It is the realization of a greater thing, Promises. Here, we will learn their concepts, and some other uses of promises and regular values ​​Dojo’s API in a unified way.

The basis of learning this section is to first learn dojo/request and dojo/Deferred knowledge, and the basic concepts of these interfaces, we will introduce a more abstract concept: promises. A promise is an object, which represents the final return value after an operation ends. The dojo/promise interface has undergone major updates and improvements after version 1.8. A promise has the following characteristics:

> It can be in one of these three states: unfulfilled, resolve, rejected

> It can only change from unfulfilled to resolved or unfulfilled to rejected Status

> The implementation is a then method, which is used to register a callback function that reminds the state to change

> The callback function cannot change the monk returned by the promise

> A promise The then method returns a new promise, which is used to provide a chain structure while keeping the value of the initial promise unchanged.

With this knowledge, let’s explore how Dojo implements promises.

Defferred as a Promise

If you think a promise sounds more like a Deferred, it means you have noticed this . In fact, the dojo/Deferred module is the main implementation of Dojo’s promise interface. The following example:

require(['dojo/request'], function(request) {// original is a Deferred var original = request.get("users-mangled.json", {handleAs: "json" });});

As mentioned before, request.get (and other Dojo Ajax help functions) returns a promise , This promise represents the final value returned when the request from the server ends. Initially, it will be in an unfulfilled state, and then change to a resolved or rejected state according to the result returned by the server.

We can register the callback function through the then method on the promise returned by the request, but we can only know that the return value of the then method has a then method, and it does not cover the return value of the then method All of it. You might think it returned the initial promise, but it actually returned a simple object that implements the promise interface. The two most commonly used methods are then and cancel. Here is an example:

require(['dojo/_base/array','dojo/dom','dojo/dom -construct','dojo/json'], function(arrayUtil, dom, domConstruct, JSON) {// result is a new promise that produces a new value var result = original.then(function(response) {var userlist = dom .byId("userlist1"); return arrayUtil.map(response, function(user) {domConstruct.create("li", {innerHTML: JSON.stringify(user) }, userlist); return {id: user[0] , username: user[1], name: user[2] }; }); });});

This then call returns a promise object, and the value of this promise object will be returned by the callback function Value setting. We can see that the value of the new promise is different from the original Deferred.

// chaining to the result promise rather than the original deferred to get our new valueresult.then(function(objs) {var userlist = dom.byId("userlist2 "); arrayUtil.forEach(objs, function(user) {domConstruct.create("li", {innerHTML: JSON.stringify(user) }, userlist); });});

Promise returned value Both are the return value of the callback function. If the callback function of the promise does not return a value, the value of the promise will be undefined. If undefined appears in your chaining, please make sure that the return value is provided in the callback function of your promise. If you don’t need to think about chaining, you don’t need to worry about whether a return value is provided.

At this point, we can check that the value of the initial Deferred has not changed:

/ / creating a list to show that the original deferred's value was untouchedoriginal.then(function(response) {var userlist = dom.byId("userlist3"); arrayUtil.forEach(response, function(user) {domConstruct.create("li ", {innerHTML: JSON.stringify(user) }, userlist); });});

As we have seen before, chaining is very powerful, and its more powerful place lies in each object in the chain Is immutable.

It should be noted that the Deferred instance includes another attribute: promise, which is an object that only implements the promise interface, but represents the return value of Deferred. The promise attribute allows you to minimize the negative impact of users calling your interface, by avoiding intentionally or unintentionally calling the resolve or reject methods, but still allowing them to obtain the original Deferred value.

dojo/when

dojo/when is a powerful function provided by Dojo, which allows you to handle promises or regular values ​​with a consistent interface. The dojo/when function has 4 parameters: a promise or regular value, an optional callback function, an optional error handling function, and an optional progress function. It performs one of the following two situations:

If the first parameter is not a promise and a callback function is provided, the value of the first parameter will be passed to the callback function and executed immediately, and return The execution result of the callback function. If no callback function is provided, the first parameter value will be returned immediately.

> If the first parameter is a promise, a callback function, error handling function, and progress function are provided in the then method of the promise, and a new promise will be returned. Set the callback function to be executed when the promise is completed.

The following is an example:

function getUserList() {return request.get("users-mangled .json", {handleAs: "json" }).then(function(response) {return arrayUtil.map(response, function(user) {return {id: user[0], username: user[1], name: user[2] }; }); });}

Assuming that the user list does not change frequently and can be cached on the client side instead of fetching them every time a function is called. In this case, because dojo/when requires a regular value or a promise, getUserList can be changed to return a promise or an array of users, and then we can use dojo/when to handle the return value:

require(['dojo/_base/array','dojo/when','dojo/request','dojo/dom' ,'dojo/dom-construct','dojo/json'], function(arrayUtil, when, request, dom, domConstruct, JSON) {var getUserList = (function() {var users; return function() {if(! users) {return request.get("users-mangled.json", {handleAs: "json" }).then(function(response) {// Save the resulting array into the users variable users = arrayUtil.map(response, function(user) {return {id: user[0], username: user[1], name: user[2] }; }); // Make sure to return users here, for valid chaining return users; });} return users; }; })();});when(getUserList(), function(users) {// This callback will be run after the request completes var userlist = dom.byId(" userlist1"); arrayUtil.forEach(users, function(user) {domConstruct.create("li", {innerHTML: JSON.stringify(user) }, userlist); }); when(getUserList(), function(user) {// This callback will run right away since it's already in cache var userlist = dom.byId("userlist2"); arrayUtil.forEach(users, function(user) {domConstruct.create("li", {innerHTML: JSON. stringify(user) }, userlist); }); });});

It can also be the API you are responsible for creating a user list, and you want to provide a clear API for your developers to The server side or an array passes you a list of users. In this case, you may use one of the following methods:

function createUserList(node, users){ var nodeRef = dom.byId(node); return when( users, function(users){ arrayUtil.forEach(users, function(user){ domConstruct.create("li", {innerHTML: JSON.stringify(user) }, nodeRef ); }); }, function(error){ domConstruct.create("li", {innerHTML: "Error: "+ error }, nodeRef);} );}var users = request.get("users-mangled. json", {handleAs: "json"}).then(function(response){ return arrayUtil.map(response, function(user){ return {id: user[0], username: user[1], name: user [2] }; });});createUserList("userlist1", users);createUserList("userlist2", [{ id: 100, username: "username100", name: "User 100" }]);As above, dojo/when allows developers to use one interface to handle synchronous and asynchronous situations at the same time, at the same time in the producer and consumer range. 

Use dojo/promise/all to process the list of promises

dojo/promise/all replaces the dojo/DeferredList module, which provides processing for one promise by combining the results of multiple promises Multiple asynchronous operation mechanisms. Sometimes, you need to get data from multiple sources in parallel and want to get a notification when all requests are over. Dojo/promise/all provides such a solution.

The use of dojo/promise/all is very simple, just pass an object or an array of Deferreds to its constructor, and the return result is an object using the same key in the passed parameter, or one and the passed in Arrays in the same order of the array, the following is an illustrative example:

require(["dojo/promise/all", "dojo/ Deferred", "dojo/request", "dojo/_base/array", "dojo/dom-construct", "dojo/dom", "dojo/json", "dojo/domReady!"],function(all, Deferred , request, arrayUtil, domConstruct, dom, JSON){ var usersDef = request.get("users.json", {handleAs: "json" }).then(function(response){ var users = {}; arrayUtil.forEach (response, function(user){ users[user.id] = user; }); return users; }); var statusesDef = request.get("statuses.json", {handleAs: "json" }); all( [usersDef, statusesDef]).then(function(results){ var users = results[0], statuses = results[1], statuslist = dom.byId("statuslist"); if(!results[0] ||! results[1]){ domConstruct.create("li", {innerHTML: "An error occurred" }, statuslist); return;} arrayUtil.forEach(statuses, function(status){ var user = users[status.userId]; domConstruct.create("li", {id: status.id, innerHTML: user.name + 'said, "'+ status.status +'"' }, statuslist); }); });});

Here, we want to get the user list and status list from the server side at the same time, by registering a callback The function returns the hash of the user ID, passes both Deferreds to dojo/promise/all, and registers a callback function for it. The callback function checks for errors. If there is no error, it will traverse the status array and match it with the user. stand up. No matter which request is executed first, dojo/promise/all will return an array of the order in which Deferreds are passed in.

Summary

Dojo's promises interface provides developers with two opportunities to create more powerful applications: the promises returned by the Deferred function are immutable, which avoids some Negative impact, and dojo/when provides a gap between promise-based and conventional value encoding. Based on this, dojo/promise/all allows you to handle multiple deferreds/promises with one callback function.

Leave a Comment

Your email address will not be published.