• Promises solve the Pyramid-of-doom problem and enable clean exception handling. Source: Adapted from Stittri, 2016.
    image
  • A chain of promises makes for cleaner code. Source: Aderinokun, 2016.
    image
  • States and transitions of a promise object. Source: MDN Web Docs, 2018.
    image
  • Illustrating different ways in which 'then' method can be exited. Source: Lawson, 2015.
    image

Promises

Improve this article. Show messages.

Summary

In asynchronous programming, it's common practice to use callbacks so that the main thread can continue with other processing rather than wait for current function to complete. When that function completes, a relevant callback function is called. Writing and maintaining asynchronous code can be difficult. Promises offer a syntax that enables better code structure and flow.

A promise is an object that's returned immediately when an asynchronous call is made even when such a call has not completed. This object is constructed and returned "with the promise" that its contents will be filled in at a later point when the asynchronous call completes. Formally,

A promise represents the eventual result of an asynchronous operation.

Promises have become popular since mid-2010s in the world of JavaScript and web development, although promises are not exclusive to JavaScript.

Milestones

1976

The concept of promises is born in the domain of parallel computing. It's called by different names: futures, promises, eventuals.

Jun
1988

MIT researchers present a paper that defines and explains promises. They describe how promises can aid asynchronous programming in distributed systems, including sequences of async calls. It references an async communication mechanism called call-streams and illustrates the concepts in Argus programming language.

Mar
2009

Discussion starts within a CommonJS Google Group for specifying an API for promises. An initial version of Promises/A is published in February 2010.

Dec
2012

Promises/A+ specification version 1.0 is released. Versions 1.1 and 1.1.1 are subsequently released in 2013 and 2014 respectively. Promises/A+ is based on the earlier Promises/A but has some omissions, clarifications and additions. Specifically, progress handling and interactive promises are omitted. The focus has been to arrive at a minimal API for interoperability. For interoperability, the specification details the behaviour of the then method of a promise object. The specification doesn't talk about how to create, fulfill or reject promises.

Jun
2015

Promise is introduced into ECMA Script 2015, 6th Edition.

Discussion

  • Why do we need promises?
    image
    Promises solve the Pyramid-of-doom problem and enable clean exception handling. Source: Adapted from Stittri, 2016.

    Asynchronous code often ends up with deeply nested callbacks. Programmers often call this callback hell or pyramid of doom. This happens because in async code we can't return values because such values are not yet ready. Likewise, we can't throw exceptions because there's no one to catch them.

    Promises solve this by flattening the program flow. Because each asynchronous call immediately returns with a promise object, this object can then be used to specify the callback functions for both success and failure cases. In particular, then method of a promise object allows us to specify the callbacks. This method also returns another promise object, thus facilitating a chain or composition of promises.

    Asynchronous code written with promises is closer in spirit to how we write synchronous code. In synchronous code, we are used to return, throw and catch statements. This functionality was lost in the world of asyn callbacks. Essentially,

    The point of promises is to give us back functional composition and error bubbling in the async world.

  • What are the essentials of a promise?
    image
    A chain of promises makes for cleaner code. Source: Aderinokun, 2016.

    A promise is a first-class object, meaning that it can be copied, passed as arguments or returned from functions. Moreover, a promise is returned even before the result of an async call is ready. This allows us to call methods of the object (such as then method) while the async call is still in progress.

    Callbacks can be specified via the then method. Two things are possible when the then method is called:

    • If the async call has already completed (or it could even be a synchronous call), the promise object will invoke the relevant callback immediately.
    • If the async call is still pending, the promise object will register the callback but call it later.

    In either case, then method returns a new promise object and this is important to enable chaining. Multiple callbacks can be added by calling then multiple times.

    Promises also simplify the handling of exceptions. Anytime an exception is thrown it can be handled by a single catch method. In fact, the catch method is a simplification of then method for exception handling.

  • What are the states and rules of transition of a promise object?
    image
    States and transitions of a promise object. Source: MDN Web Docs, 2018.

    A promise can be in one of three states:

    • pending: This is the initial state.
    • fulfilled: This is entered if the execution succeeds. The promise is fulfilled with a value.
    • rejected: This is entered if execution fails. Rejection comes with a reason.

    A promise that's either fulfilled or rejected is said to be settled. This is an apt term since a promise that's settled is an immutable object. It's value (when fulfilled) or reason (when rejected) cannot change. Immutability is important so that consumers of the promise are prevented from introducing side effects.

    A promise is said to be resolved if it's either settled or "locked in" to the state of another promise. Once resolved, any attempt to resolve it again will have no effect on the promise. An unresolved promise, a promise that's not resolved, is in pending state.

  • What are the methods of a promise object?

    The Promises/A+ standard specifies then method to access the eventual value or reason. For interoperability, no other method needs to be specified.

    However, it's common for other standards or implementations to have other methods. For example, ECMAScript 2015 specifies:

    • Methods all, race, reject and resolve of Promise
    • Methods then, catch and finally of a Promise.prototype

    Among the non-standard methods are Promise.denodeify, Promise.prototype.done, Promise.prototype.finally and Promise.prototype.nodeify. GuzzleHttp's Promise implementation in PHP provides methods otherwise, wait, getState and cancel. Bluebird adds useful methods such as map, some, any, filter, reduce, each and props.

  • Could you share some details of the then method?

    A promise must have a then method that accepts two arguments and returns another promise: q = p.then(onFulfilled, onRejected), where the following hold true for promises p and q:

    • Arguments onFulfilled and onRejected are both optional
    • If onFulfilled is a function, it's called once when p is fulfilled
    • If onRejected is a function, it's called once when p is rejected
    • Promise q is resolved with value x if either function onFulfilled or onRejected returns x
    • Promise q is rejected with reason e if either function onFulfilled or onRejected throws an exception e
    • If onFulfilled is not a function, q will be fulfilled with the value of p when fulfilled
    • If onRejected is not a function, q will be rejected with the reason of p when rejected

    For interoperability, non-promises can be treated as promises if they provide the then method, for example, using duck typing. Such an object is called a thenable.

  • Can you give some use cases where promises might simplify my code?

    Promises can enable us to sequentially call a bunch of async functions. We can handle all errors in a single code block if we wish to do so. We can trigger multiple async calls and do further processing only when all of them have completed; or exit if any one of them throws an exception; or handle the first one that completes and ignore the rest. Promises enable us to retry async calls more easily.

  • What are some tools for working with promises?

    If your browser doesn't support promises, a polyfill will be needed. One option is to use promise-polyfill. Another polyfill is called es6-promise.

    Bluebird is a JS library for promises. It can be used in Node.js and browsers. Using "promisification", it can convert old API into promise-aware API. Another useful library is Q. Alternatives include promise, lie, when and RSVP. A comparison of these promise libraries by size and speed is available.

    Mocha, Chai and Sinon.JS are three suggested tools for testing asynchronous program flow. Mocha and Chai in combination work nicely for testing promises.

  • Can you give some tips for developers coding promises?
    image
    Illustrating different ways in which 'then' method can be exited. Source: Lawson, 2015.

    Here are some tips, for beginners in particular:

    • It's possible to write promise-based code in the manner of callback-style nested code. This is bad practice. Instead use a promise chain.
    • Always handle errors using either then(null, onRejected) or catch().
    • Avoid using the old-style deferred pattern that used to be common with jQuery and AngularJS.
    • Always return a value or throw an exception from inside then and catch methods. This is also handy for converting synchronous code into promisey code.
    • Promise.resolve() can wrap errors that occur in synchronous code. Be sure to use a catch to handle all errors.
    • Note that then(onFulfilled).catch(onRejected) is different from then(onFulfilled, onRejected). The latter code will not catch exceptions that may occur inside onFulfilled function.
  • I have a bunch of promises I wish to call sequentially. Can I use a for loop?

    This is an interesting case where each promise invokes (presumably) some asynchronous code and yet we wish to wait for each asynchronous call to complete before we start the next promise. A promise executes as soon as it's created. If you have a bunch of promises to be called in sequence, any loop construct such as forEach will not work. Instead, use a chain of promises, that is, each promise is constructed within the then method of the previously fulfilled promise.

Sample Code

  • // ====================================================================
    // Creating promises
    // ====================================================================
    // Source: https://tech.io/playgrounds/347/javascript-promises-mastering-the-asynchronous/the-promise-class
    // In Q library
    var promise =  Q.Promise(function(resolve, reject) {
    });
     
    // AngularJS
    var promise =  $q(function(resolve, reject) {
    });
     
    // AJAX in jQuery
    var promise = $.get('/foo/bar/common');
     
     
    // ====================================================================
    // Basic examples
    // ====================================================================
    // Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
    // Old-style callbacks
    doSomething(function(result) {
      doSomethingElse(result, function(newResult) {
        doThirdThing(newResult, function(finalResult) {
          console.log('Got the final result: ' + finalResult);
        }, failureCallback);
      }, failureCallback);
    }, failureCallback);
     
    // Promise-based code using chaining
    doSomething().then(function(result) {
      return doSomethingElse(result);
    })
    .then(function(newResult) {
      return doThirdThing(newResult);
    })
    .then(function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    })
    .catch(failureCallback);
     
    // Same as above but using arrow functions
    doSomething()
    .then(result => doSomethingElse(result))
    .then(newResult => doThirdThing(newResult))
    .then(finalResult => {
      console.log(`Got the final result: ${finalResult}`);
    })
    .catch(failureCallback);
     
    // We can also chain after a catch
    new Promise((resolve, reject) => {
        console.log('Initial');
        resolve();
    })
    .then(() => {
        throw new Error('Something failed');
        console.log('Do this');
    })
    .catch(() => {
        console.log('Do that');
    })
    .then(() => {
        console.log('Do this whatever happened before');
    });
     
     
    // ====================================================================
    // catch(failureCallback) is a simplified syntax for then(null, failureCallback)
    // ====================================================================
    doSomething()
    .then(result => doSomethingElse(result))
    .catch(failureCallback);
     
    // Equivalent to using a catch()
    doSomething()
    .then(result => doSomethingElse(result))
    .then(null, failureCallback);
     
    // Passing both callbacks within a single then()
    // :this is not same as above examples
    // :any exception thrown by doSomethingElse() will not be caught!
    // :failureCallback will only catch exceptions thrown by doSomething()
    doSomething()
    .then(result => doSomethingElse(result), failureCallback);
     
    // Examples you can run on your browser's console
    // Note that "KO2" will print first since it's handled with the first then()
    Promise.resolve('OK1').then(result => doSomethingElse(result)).catch((err) => console.log(err));
    Promise.reject('KO1').then(result => doSomethingElse(result)).catch((err) => console.log(err));
    Promise.resolve('OK2').then(result => doSomethingElse(result), (err) => console.log(err));
    Promise.reject('KO2').then(result => doSomethingElse(result), (err) => console.log(err));
     
     
    // ====================================================================
    // We can wrap old callback API (such as setTimeout) with a Promise
    // ====================================================================
    const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
     
    // We throw an error
    wait(10000)
    .then(() => {
        console.log("10 seconds");
        throw "KO";
    })
    .catch((err) => console.log(err + " Something failed."));
     
    // We explicitly return a rejected promise
    wait(10000)
    .then(() => {
        console.log("10 seconds");
        return Promise.reject("KO");
    })
    .catch((err) => console.log(err + " Something failed."));
     
    // BAD: we forget to return the rejected promise, which will be uncaught!
    wait(10000)
    .then(() => {
        console.log("10 seconds");
        Promise.reject("KO");
    })
    .catch((err) => console.log(err + " Something failed."));
     
     
    // ====================================================================
    // Special case
    // ====================================================================
    // Source: https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
    // Note that when a promise is fulfilled with a value,
    // the next promise in the chain is fulfilled with the same value
    // when it doesn't take a function as it argument.
    // Hence this code with print "foo" and not "bar"
    Promise.resolve('foo')
    .then(Promise.resolve('bar'))
    .then((result) => { console.log(result); });`
     
     
    // ====================================================================
    // jQuery
    // ====================================================================
    // Convert jQuery's promise to a standardized promise
    // Source: https://www.promisejs.org/
    var jQueryPromise = $.ajax('/data.json');
    var realPromise = Promise.resolve(jQueryPromise);

References

  1. AWS Docs. 2018. "Class Promise." AWS SDK for PHP 3.x API Documentation. Accessed 2018-02-12.
  2. Aderinokun, Ire. 2016. "JavaScript Promises 101." bitsofcode. July 12. Accessed 2018-02-12.
  3. Baker, Henry G. and Carl Hewitt. 1977. "The Incremental Garbage Collection of Processes." ACM Proceedings of the Symposium on Artificial Intelligence Programming Languages, pp. 55–59. Accessed 2018-02-12.
  4. Bluebird Docs. 2016a. "Promisification." API Reference. August 31. Accessed 2018-02-08.
  5. Bluebird Docs. 2016b. "API Reference." August 31. Accessed 2018-02-12.
  6. CommonJS Google Group. 2009. "Promise API Proposal." Google Groups. Accessed 2018-02-08.
  7. CommonJS Wiki. 2013. "Promises/A." January 24. Accessed 2018-02-08.
  8. Denicola, Domenic. 2012. "You're Missing the Point of Promises." Hidden Variables. October 14. Accessed 2018-02-08.
  9. ECMA International. 2015. "ECMAScript® 2015 Language Specification." 6th Edition. June. Accessed 2018-02-08.
  10. Foged, Poul. 2014. March 3. "JavaScript Promises – a comparison of libraries." Accessed 2018-02-12.
  11. Google GitHub. 2018. "Promises." GitHub. February 11. Accessed 2018-02-11.
  12. Hakes, Taylor. 2018. "promise-polyfill." GitHub. February 7. Accessed 2018-02-08.
  13. Lawson, Nolan. 2015. "We have a problem with promises." PouchDB. May 18. Accessed 2018-02-08.
  14. Lindesay, Forbes. 2018. "API Reference." February 2. Accessed 2018-02-08.
  15. Liskov, Barbara and Liuba Shrira. 1988. "Promises: Linguistic Support for Efficient Asynchronous Procedure Calls in Distributed Systems." ACM Proceedings of the SIGPLAN '88 Conferenece on Programming Language Design and Implementation, pp. 260-267. June 22-24. Accessed 2018-02-08.
  16. MDN Web Docs. 2018a. "Promise." January 21. Accessed 2018-02-08.
  17. MDN Web Docs. 2018b. "Using promises." January 21. Accessed 2018-02-08.
  18. Promises/A+. 2018a. Accessed 2018-02-08.
  19. Promises/A+. 2018b. "Promises/A+ Changelog." Accessed 2018-02-08.
  20. Promises/A+. 2018c. "Differences from Promises/A." Accessed 2018-02-08.
  21. Stittri, Mek. 2016. "Moving to fullstack end-to-end test automation with Node.js part 1 - Selenium Webdriver." Medium. January 3. Accessed 2018-02-08.
  22. Venkat R. 2017. "Tools for Promises Unit Testing." Tech.io. December 22. Accessed 2018-02-08.

Milestones

1976

The concept of promises is born in the domain of parallel computing. It's called by different names: futures, promises, eventuals.

Jun
1988

MIT researchers present a paper that defines and explains promises. They describe how promises can aid asynchronous programming in distributed systems, including sequences of async calls. It references an async communication mechanism called call-streams and illustrates the concepts in Argus programming language.

Mar
2009

Discussion starts within a CommonJS Google Group for specifying an API for promises. An initial version of Promises/A is published in February 2010.

Dec
2012

Promises/A+ specification version 1.0 is released. Versions 1.1 and 1.1.1 are subsequently released in 2013 and 2014 respectively. Promises/A+ is based on the earlier Promises/A but has some omissions, clarifications and additions. Specifically, progress handling and interactive promises are omitted. The focus has been to arrive at a minimal API for interoperability. For interoperability, the specification details the behaviour of the then method of a promise object. The specification doesn't talk about how to create, fulfill or reject promises.

Jun
2015

Promise is introduced into ECMA Script 2015, 6th Edition.

Tags

See Also

  • Asynchronous programming
  • Concurrent programming
  • Revealing constructor pattern
  • Futures

Further Reading

  1. Rauschmayer, Alex. 2017. "Promises for asynchronous programming." Exploring ES6. Section 25. Accessed 2018-02-08.
  2. Arya, Naren. 2017. "Writing neat asynchronous Node JS code with Promises." DEV.BITS(). June 4. Accessed 2018-02-08.
  3. Denicola, Domenic. 2014. "The Revealing Constructor Pattern." Hidden Variables. February 14. Accessed 2018-02-08.
  4. Kundel, Dominik. 2016. "A quick guide to JavaScript Promises." Twilio Blog. October 3. Accessed 2018-02-08.

Top Contributors

Last update: 2018-02-12 10:34:23 by hemanthSK
Creation: 2017-04-26 13:02:57 by arvindpdmn

Article Stats

1553
Words
0
Chats
2
Authors
3
Edits
2
Likes
1230
Hits

Cite As

Devopedia. 2018. "Promises." Version 3, February 12. Accessed 2018-10-18. https://devopedia.org/promises
BETA V0.17