The idea of promises is a key concept in programming. This discussion focuses on AngularJS Promises, which have some unique characteristics compared to other types of promises, but the fundamental concept remains consistent across different libraries.
Understanding Asynchronous Processes
If you are already familiar with this topic, feel free to skip ahead to the next section; otherwise:
In typical code execution, tasks are carried out sequentially in the following manner:
object.method() // First,
variable = "something"; // Second,
for(var i=0; i<2; i++) {
resp = object.makeHttpRequest();
console.log(resp.data + " was #" + i);
} // Third,
console.log("Done"); // Last.
Each step waits for the previous one to complete before proceeding. This can pose an issue when a task, like a loop making HTTP requests, takes a significant amount of time to finish. The entire process would be halted until that particular task completes, causing inefficiencies.
Node.js addresses this by default through a callback pattern. When invoking a blocking function (one that requires considerable time, such as reading from disk or executing an HTTP request), a callback function is registered to execute upon completion. This method allows other code to run concurrently while the blocking operation proceeds.
Many Node.js developers find this approach messy and opt for a cleaner solution offered by frameworks like AngularJS - using Promises. Promises adhere to a standardized Promise Pattern.
Familiarity with Asynchronous Concepts
Promises bear resemblance to callbacks conceptually, but are more organized and offer better control. Consider the following scenario:
var url = getUrlFunction();
makeHttpRequest(url, function onResponse(data) {
dataHandler(data);
console.log("done");
}, function onError(err) {
errHandler(err);
console.log("uh oh");
});
showTheUserWeAreLoading();
// Or in node.js
var url = getUrlFunction();
makeHttpRequest(url, function onResponse(err, data) {
(err) ? handleErr(err): null;
dataHandler(data);
console.log("done");
});
showTheUserWeAreLoading();
In the above example, it may not be clear that the showTheUserWeAreLoading
function could execute before the HTTP request completes, leading to confusion during code review.
By replacing the callback-based implementation with a promise-based one, the scenario becomes:
var url = getUrlFunction(), prom = makeHttpRequest(url);
showTheUserWeAreLoading();
prom.then(function onSuccess(data) {
dataHandler(data);
console.log("done");
}, function onError(err) {
errHandler(err);
console.log("uh oh");
});
The promise object aids in monitoring the operation's progress. Handlers are assigned for two states: Fulfilled or Rejected.
Note that makeHttpRequest
serves as a substitute for $http()
in AngularJS or $.ajax
in jQuery. Before the standardization of promises in the ECMAScript specification (referenced here), various libraries had their distinct approaches to handling asynchronous operations. AngularJS previously employed the
.success(<function>).error(<function>)
naming convention, whereas jQuery utilized
.done(<function>).fail(<function>)
. These conventions have long been deprecated, eliminating disparities among libraries (thanks to ECMAScript).