Why are callbacks more "tightly coupled" than promises? Why are callbacks more "tightly coupled" than promises? jquery jquery

Why are callbacks more "tightly coupled" than promises?


A promise is an object that represents the result of an asynchronous operation, and because of that you can pass it around, and that gives you more flexibility.

If you use a callback, at the time of the invocation of the asynchronous operation you have to specify how it will be handled, hence the coupling. With promises you can specify how it will be handled later.

Here's an example, imagine you want to load some data via ajax and while doing that you want to display a loading page.

With callbacks:

void loadData = function(){  showLoadingScreen();  $.ajax("http://someurl.com", {    complete: function(data){      hideLoadingScreen();      //do something with the data    }  });};

The callback that handles the data coming back has to call hideLoadingScreen.

With promises you can rewrite the snippet above so that it becomes more readable and you don't have to put the hideLoadingScreen in the complete callback.

With promises

var getData = function(){  showLoadingScreen();  return $.ajax("http://someurl.com").promise().always(hideLoadingScreen);};var loadData = function(){  var gettingData = getData();  gettingData.done(doSomethingWithTheData);}var doSomethingWithTheData = function(data){ //do something with data};

UPDATE: I've written a blog post that provides extra examples and provides a clear description of what is a promise and how its use can be compared to using callbacks.


The coupling is looser with promises because the operation doesn't have to "know" how it continues, it only has to know when it is ready.

When you use callbacks, the asynchronous operation actually has a reference to its continuation, which is not its business.

With promises, you can easily create an expression over an asynchronous operation before you even decide how it's going to resolve.

So promises help separate the concerns of chaining events versus doing the actual work.


I don't think promises are more or less coupled than callbacks, just about the same.

Promises however have other benefits:

  • If you expose a callback, you have to document whether it will be called once (like in jQuery.ajax) or more than once (like in Array.map). Promises are called always once.

  • There's no way to call a callback throwing and exception on it, so you have to provide another callback for the error case.

  • Just one callback can be registered, more than one for promises, and you can register them AFTER the event and you will get called anyway.

  • In a typed declaration (Typescript), Promise make easier to read the signature.

  • In the future, you can take advantage of an async / yield syntax.

  • Because they are standard, you can make reusable components like this one:

     disableScreen<T>(promiseGenerator: () => Promise<T>) : Promise<T> {     //create transparent div     return promiseGenerator.then(val=>     {        //remove transparent div        return val;     }, error=>{         //remove transparent div         throw error;     }); } disableScreen(()=>$.ajax(....));

More on that: http://www.html5rocks.com/en/tutorials/es6/promises/

EDIT:

  • Another benefit is writing a sequence of N async calls without N levels of indentation.

Also, while I still don't think it's the main point, now I think they are a little bit more loosely coupled for this reasons:

  • They are standard (or at least try): code in C# or Java that uses strings are more lousy coupled than similar code in C++, because the different implementations of strings there, making it more reusable. Having an standard promise, the caller and the implementation are less coupled to each other because they don't have to agree on a (pair) of custom callbacks with custom parameters orders, names, etc... The fact that there are many different flavors on promises doesn't help thought.

  • They promote a more expression-based programming, easier to compose, cache, etc..:

      var cache: { [key: string] : Promise<any> };  function getData(key: string): Promise<any> {      return cache[key] || (cache[key] = getFromServer(key));   }

you can argue that expression based programming is more loosely coupled than imperative/callback based programming, or at least they pursue the same goal: composability.