In Node.js, there are three common approaches to handle API response: synchronous, callback-based, and promise-based.
Node.js has gradually shifted towards Promise-based APIs and introduced the util.promisify
utility to convert callback-based functions into Promise-based ones. Additionally, the introduction of async/await
syntax in newer versions of Node.js simplifies working with Promises, making asynchronous code resemble synchronous code in terms of readability and flow control.
Synchronous APIs, also known as blocking APIs, are programming interfaces that perform operations in a synchronous manner. In Node.js, synchronous APIs are typically suffixed with “Sync” to differentiate them from their asynchronous counterparts.
const fs = require('fs');
// Synchronous API
const data = fs.readFileSync('file.txt', 'utf8');
console.log(data);
console.log('Done reading file');
When using synchronous APIs, the execution of code is halted until the operation is completed. The program waits for the API call to finish and then continues with the next line of code. This blocking behavior means that other operations or tasks cannot proceed until the synchronous operation is finished. Here are a few characteristics of synchronous APIs:
It’s worth noting that using synchronous APIs can impact the responsiveness and scalability of your application, particularly in scenarios with heavy I/O or concurrent operations. Thus, it is generally recommended to favor asynchronous or non-blocking APIs in Node.js for better performance and scalability. However, there are some cases where synchronous APIs can be useful, such as:
In general, synchronous APIs should be avoided or minimized in Node.js, unless it is necessary for some reason.
Callback-based APIs, also known as asynchronous APIs or Node.js-style callbacks, are a common pattern in Node.js for handling asynchronous operations. These APIs rely on passing callback functions as arguments to asynchronous functions, which are then invoked once the operation is completed or an error occurs.
const fs = require('fs');
// Callback-based API
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) {
console.error(err);
} else {
console.log(data);
}
});
console.log('Done reading file');
Here are the key characteristics of callback-based APIs:
null
or undefined
.It’s important to note that callback-based APIs can lead to callback hell, making code harder to read and maintain. To mitigate this issue, other asynchronous patterns like Promises or async/await syntax are often preferred in modern Node.js applications.
Promise-based APIs, introduced in ECMAScript 2015 (ES6), are an alternative approach to handling asynchronous operations in JavaScript and Node.js. Promises provide a more structured and readable way to work with asynchronous code compared to callback-based APIs.
const fs = require("fs");
(async () => {
try {
await fs.promises.access("foo.txt", fs.constants.F_OK);
// Do something
} catch (err) {
// Handle error
}
})();
Here are the key characteristics of Promise-based APIs:
.then()
to perform sequential operations or handle subsequent transformations on the results..catch()
, allowing for a centralized error handling approach.Promise.all()
or Promise.race()
to execute multiple asynchronous operations concurrently or sequentially.With Promise-based APIs, you can handle asynchronous operations in a more readable and structured manner, making the code easier to understand and maintain. Promise-based APIs are widely adopted in modern JavaScript and Node.js development, and they serve as the foundation for other async patterns like async/await
.