jQuery 3 - Implementing Promises
Written by Ian Elliot   
Monday, 29 May 2017
Article Index
jQuery 3 - Implementing Promises
JavaScript Promises
Extending the Race Function

jquery2cover

Extending the Race Function

Of course, you could add the race function as a method to the promise object. 

To extend the race function to work with any number of promises can be done in two ways, either jQuery's multiple parameters or JavaScript's iterable. In practice both are very similar to implement.

To implement the jQuery approach we simply use the arguments array:

function race() {
 var d = $.Deferred();
 $.map(arguments,
         function (p) {
           p.then(function (value) {
                    d.resolve(value);
                   },
                  function (error) {
                    d.reject(error);
                   }
              );
          }
      );
 return d.promise();
}

You can see the general idea. For each promise in the arguments array we use then to attach a resolve and reject handler. This version can be called as before with separate arguments. To implement the JavaScript version we simply have to include an explicit array parameter and alter the way the function is called:

function race(args) {
 var d = $.Deferred();
 $.map(args, 
         function (p) {
           p.then(function (value) {
                    d.resolve(value);
                   },
                  function (error) {
                    d.reject(error); 
                   }
              );
          }
      );
 return d.promise(); 
}

and to call the function:

getTime();
race([delay(1000), delay(2000)]).then(getTime);

Notice that now there is a single array parameter.

Of the two, I think I prefer the arguments approach used by jQuery. 

This is an implementation of the standard race function, but it is generally held that it isn't very useful as it will return the first function to complete, even if it rejects. What would be better is an implementation of any, which is found in some promise libraries, that returns the first successful result or a reject if there is no successful function:

function any() {
 var d = $.Deferred();
 var number = arguments.length;
 $.map(arguments, function (p) {
           p.then(function (value) {
                     d.resolve(value);
                    },
                  function (error) {
                     if (--number === 0) {
                      d.reject(error);
                     }
                    });
                   }
       );
 return d.promise();
}

This works in a very similar way to race but we now maintain a count of the number of promises included in the arguments. Each time a promise is rejected we reduce the count by one. If the count reaches zero then all of the promises have been rejected and we change the state of the returned promise to rejected. Notice that as long as one of the promises resolves the returned promise resolves. As before, we make no attempt to cancel any no longer wanted promises or tasks. 

As a final example, and one that is useful in practice, let's explore a timeOut function. One of the problems with promises is that they don't have a timeout. If a promise isn't resolved or rejected then it will continue to be pending forever. The following function takes a promise and returns a new promise that will reject if the original promise doesn't accept or reject within the specified timeout: 

function timeOut(p, t) {
 var d = $.Deferred();
 p.then(function (value) {
             d.resolve(value)
         },
        function (error) {
             d.reject(error)
         }

 );
 setTimeout(function () {
             d.reject("timeout");
             }
            ,t);
 return d.promise();
}

Again, this is very simple. All that happens is that a new Deferred is created and is resolved if the original promise resolves and is rejected if the setTimeout is triggered first. This would be easier to use as a method added to the promise object because then it could be used with chaining. 

Beyond Promises

Once you understand the way promises can be used to trigger other promises the only problem is that you will go too far with the idea. Keep it simple and only write the code you actually need. Promises are a reasonably good solution to the asynchronous problem, but the soon to be common async and await is so much better. Promises are a solution for now, but not for the longer term future. 

Summary

  • Only the code that created the promise should be able to set the promise's state.  The earliest solution to this problem of keeping the internal state private was to use a two-object solution. A Deferred object was used by the promise creator to manage the promise. 

  • A Deferred object has resolve and reject methods that change the state of its associated promise object. 

  • To use a Deferred you should create it and allow your asynchronous code to use resolve and reject when it completes and return a promise object to the client code. 

  • JavaScript standard promises keep the resolve and reject private without the use of a management object, i.e. a Deferred. Instead they provide resolve and reject as private functions that an be accessed via the constructor.
     
  • To create a standard promise you pass the constructor a function with resolve and reject as parameters. The code in the function runs the asynchronous task and calls either resolve or reject when the task finishes. The constructor calls the function immediately and this provides access to the private resolve and reject methods.

  • If you are consuming promises provided by other software, jQuery in particular, there is little reason to convert whatever flavour of promise is being used to a JavaScript standard promise.

  • If you are creating functions that return promises then use JavaScript standard promises unless you need to support IE when jQuery Deferreds are preferable.

  • It seems natural to create functions with parameters that return promises but these are difficult to use in a then because of the need to pass a reference and not execute the function. The standard solution is to use currying to set the parameters. 

  • It is easy to create a range of functions that combine a set of promises into a single promise that is resolved when some subset of the original promises are. 

 

 

Just jQuery
Events, Async & AJAX

Is now available as a print book: Amazon 

jquery2cover

 

Contents

  1. Events, Async & Ajax (Book Only)
  2. Reinventing Events
  3. Working With Events
  4. Asynchronous Code
  5. Consuming Promises
  6. Using Promises 
  7. WebWorkers
  8. Ajax the Basics - get
  9. Ajax the Basics -  post
  10. Ajax - Advanced Ajax To The Server
  11. Ajax - Advanced Ajax To The Client
  12. Ajax - Advanced Ajax Transports And JSONP
  13. Ajax - Advanced Ajax The jsXHR Object
  14. Ajax - Advanced Ajax Character Coding And Encoding 

Also Available:

buy from Amazon

smallcoverjQuery

 

Advanced Attributes

 

Banner


JavaScript Canvas - Fetch API

Working with lower-level data is very much part of graphics. This extract from Ian Elliot's book on JavaScript Graphics looks at how to use typed arrays to access graphic data.



JavaScript Jems - The Inheritance Tax

JavaScript should not be judged as if it was a poor version of the other popular languages - it isn't a Java or a C++ clone. It does things its own way.  In particular, it doesn't do inheritance  [ ... ]


Other Articles

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

espbook

 

Comments




or email your comment to: comments@i-programmer.info

 

 



Last Updated ( Thursday, 05 May 2022 )