In this Single Page Application (SPA) era, JavaScript has become increasingly complex, and handling asynchronous operations has become more crucial than ever. To manage this complexity, developers started embracing Promises. Among the available solutions, Q.js became one of the most popular libraries that helped simplify and manage asynchronous JavaScript code effectively.
In this blog post, we’ll explore real practical example of Q.js that I have used so far.
1. Basic Example – Creating and Resolving a Promise
The simplest Q.js example uses a deferred object.
function getData() {
var deferred = Q.defer();
setTimeout(function () {
deferred.resolve("Data loaded!");
}, 1000);
return deferred.promise;
}
getData()
.then(function (result) {
console.log(result); // Output: Data loaded!
});
Here,
Q.defer()creates a deferred object- You manually resolve or reject the promise
.then()handles the result
This pattern was very common before ES6.
2. Rejecting a Promise (Error Example)
function doTask() {
var deferred = Q.defer();
setTimeout(function () {
deferred.reject("Something went wrong!");
}, 1000);
return deferred.promise;
}
doTask()
.then(function (result) {
console.log(result);
})
.catch(function (err) {
console.error("Error: " + err);
});
Good practice
Use .catch() or .fail() for errors.
3. Chaining Promises
Q.js makes it easy to chain multiple asynchronous steps.
function step1() {
return Q("Step 1 done");
}
function step2() {
return Q("Step 2 done");
}
function step3() {
return Q("Step 3 done");
}
step1()
.then(step2)
.then(step3)
.then(function (result) {
console.log(result); // Final message from step3
});
Why chaining?
- Avoids nested callbacks
- Each step returns a Q promise
4. Working With Multiple Promises – Q.all
function fetchUser() {
return Q.delay(500).then(() => "User loaded");
}
function fetchOrders() {
return Q.delay(800).then(() => "Orders loaded");
}
function fetchCart() {
return Q.delay(300).then(() => "Cart loaded");
}
Q.all([fetchUser(), fetchOrders(), fetchCart()])
.then(function (result) {
console.log(result);
// Output: ["User loaded", "Orders loaded", "Cart loaded"]
});
When to use Q.all ?
- Load multiple async resources together
- Run tasks in parallel
5. Converting Node.js Callback Functions- Q.nfcall
Node.js functions usually follow the signature:
function(err, result)Q.js can convert them into promises very easily.
var fs = require("fs");
Q.nfcall(fs.readFile, "data.txt", "utf8")
.then(function (data) {
console.log(data);
})
.catch(function (err) {
console.error(err);
});
Why this is helpful?
- No need to manually wrap callback functions
- Cleaner, promise-based code
6. Delays and Time-Based Promise – Q.delay
A common Q.js utility:
Q.delay(2000).then(() => {
console.log("Executed after 2 seconds");
});Use cases
- Throttling
- Waiting between API retries
- Simulating network delays
7. Using Q.spread for Arrays of Results
Q.js allows you to pass array results as separate arguments:
function getName() {
return Q("John Doe");
}
function getAge() {
return Q(30);
}
Q.all([getName(), getAge()])
.spread(function (name, age) {
console.log(name + " is " + age + " years old.");
});
Why Spread?
More readable than indexing:
// Without spread
results[0], results[1]8. Creating Immediately Resolved Promises
var promise = Q("Instant value");
promise.then(function (value) {
console.log(value); // Output: Instant value
});When useful?
- Returning standardized promise results
- Testing promise flows
9. Wrapping Synchronous Code in a Promise – Q.fcall
function calculate() {
return 10 * 5;
}
Q.fcall(calculate)
.then(function (result) {
console.log(result); // 50
});
Why?
Sometimes you want sync functions to behave like async ones.
10. Promisifying Your Own Callback Functions
function getRandomNumber(callback) {
setTimeout(() => {
callback(null, Math.random());
}, 1000);
}
var getRandomNumberAsync = Q.denodeify(getRandomNumber);
getRandomNumberAsync()
.then(function (num) {
console.log("Random:", num);
});