This question already has an answer here:
- Aren't promises just callbacks? 7 answers
While playing with javascript asynchronous error-catch mechanisms, I ended up asking myself what is the difference between using a promise instead of a callback, beside the fact promises are maybe more sugar syntactic.
For example, lets consider
function setPromise() {
var message = "awesome";
var deferred = new $.Deferred();
setTimeout(function(){deferred.resolve(message)},3000);
return deferred.promise();
}
var promise = setPromise();
promise.done(function (message) {
console.log("promise done with message : " + message);
});
and
function setCallback(doneCallback) {
var message = "awesome";
setTimeout(function(){doneCallback(message)},3000);
}
setCallback(callback)
function callback(message) {
console.log("callback done with message : " + message);
}
Both act as closure, both allow parameters to be sent back, etc.
So what are the differences?
Promises are built on top of callbacks. The latter are more primitive, more general, and take more work when you need to do something complex.
For your example, they do pretty much the same thing. However, let's say you want to have three things resolve simultaneously (imagine requesting three resources by AJAX at the same time), and continuing when all three are done. It is trivial to do with promises, since essentially nothing changes; but with callbacks, you need to set up some flags/counters, and recognise the success and fail states yourself - much more work.
Semantically, there really is no real difference between the two pieces of code. The message is provided to the callback sometime after a call to an initial function.
From a design standpoint, people tend to favour promises as they usually lead to easier to follow code. This is especially true where the callback is processing the result of some long-running function. Consider the following two slow-running functions:
Writing code that uses the results of these two long-running functions is pretty hairy:
Notice how each link in the chain of long-running functions leads to another nesting level. With promise code, you tend not to have this problem:
Also, with promise code, there tends to be a cleaner separation of concerns. The code that does the slow-running operation doesn't know how the result will be used: It simply returns something that represents that deferred result and lets the caller deal with it.
A good application of this you deal with it design surrounds exception handling. Slow running operations could
.resolve()
the promise, but they may also.reject()
where something goes wrong. This rejection can be handled using.fail()
as follows:Here, where the caller of the slow-running-operation doesn't care about errors, it can simply ignore them.
There are several other benefits of promise programming:
Most libraries that support promises provide a way to treat both regular functions and functions that return a promise the same way. They usually accomplish this by providing a function called
when()
. This provides a really great way to test promise code, or allow a slow function to be changed to a promise-returning-one w/o affecting callers.Most libraries that support promises also provide functions for emulating more traditional control flow using promises. For example, the Q library provides
allSettled(list)
, which takes a list of promises and returns a promise which resolves when all promises in the list have completed.That said, as the other answer stated, promises come with a bit of overhead. Where you're not doing intense chaining, or error handling, or where you're using callbacks strictly for control-flow, you may be better off just passing functions around.