Streams and asynchronous nature are things that makes Node so special and efficient. Although high chances you may never have to make a synchronous HTTP request in Node, yet knowing this will give you a better understanding of Node and Javascript.
Synchronous-style HTTP requests are possible in Node with the use of Javascript Promises, along with the concepts of async and await. This tutorial assumes you know a bit if these, but if not you can refer to these resources for understanding them :
Using Javascript Promises to Make HTTP Request
Consider sending a normal GET request. The request is sent to the server asynchronously, and the response is collected asynchronously.
// some code
// will be executed asynchronously
http.get('http://www.usefulangle.com/api?api_key=554545', (response) => {
let chunks_of_data = [];
response.on('data', (fragments) => {
chunks_of_data.push(fragments);
});
response.on('end', () => {
let response_body = Buffer.concat(chunks_of_data);
// response body
console.log(response_body.toString());
});
response.on('error', (error) => {
console.log(error);
});
});
// some code
Now let us re-write the above code in a bit synchronous syntax, using Javascript Promises.
// some code
// define the promise
let request_call = new Promise((resolve, reject) => {
http.get('http://www.usefulangle.com/api?api_key=554545', (response) => {
let chunks_of_data = [];
response.on('data', (fragments) => {
chunks_of_data.push(fragments);
});
response.on('end', () => {
let response_body = Buffer.concat(chunks_of_data);
// promise resolved on success
resolve(response_body.toString());
});
response.on('error', (error) => {
// promise rejected on error
reject(error);
});
});
});
// promise resolved or rejected asynchronously
request_call.then((response) => {
console.log(response);
}).catch((error) => {
console.log(error);
});
// some code
The code still executes asynchronously. To give it a feel of synchronous style where execution of code following will be prevented, we will need to use async and await.
Making Javascript to Wait using async and await
The await operator can be placed before a Promise and makes Javascript to wait till the time promise is resolved or rejected. await can only be used inside an async function.
Inside the async function the Promise will be "awaited" to get either resolved or rejected. Until this happens, code execution inside the async function will not move forward.
So now in order to adjust the code, we will need to create a separate async function in which we will wait for the HTTP request to be made and full response to come.
// async function to make http request
async function makeSynchronousRequest() {
try {
// http_promise is a Promise
// "response_body" will hold the response if the Promise is resolved
let response_body = await http_promise;
}
catch(e) {
// if the Promise is rejected
console.error(e);
}
}
// anonymous async function to execute some code synchronously after http request
(async function () {
// wait to http request to finish
await makeSynchronousRequest();
// below code will be executed after http request is finished
// some code
})();
We are wrapping await inside an immediately-invoked async function so that it executes immediately.
The try .. catch block has been used to handle in case the Promise is rejected.
Complete Code
Combining the snippets from above, we have the complete code that allows us to make synchronous-style HTTP requests from Node.
const http = require('http');
// function returns a Promise
function getPromise() {
return new Promise((resolve, reject) => {
http.get('http://www.usefulangle.com/api?api_key=554545', (response) => {
let chunks_of_data = [];
response.on('data', (fragments) => {
chunks_of_data.push(fragments);
});
response.on('end', () => {
let response_body = Buffer.concat(chunks_of_data);
resolve(response_body.toString());
});
response.on('error', (error) => {
reject(error);
});
});
});
}
// async function to make http request
async function makeSynchronousRequest(request) {
try {
let http_promise = getPromise();
let response_body = await http_promise;
// holds response from server that is passed when Promise is resolved
console.log(response_body);
}
catch(error) {
// Promise rejected
console.log(error);
}
}
console.log(1);
// anonymous async function to execute some code synchronously after http request
(async function () {
// wait to http request to finish
await makeSynchronousRequest();
// below code will be executed after http request is finished
console.log(2);
})();
console.log(3);
Note the order of execution of console.log() :
1
3
2
async Functions Execute Asynchronously
async functions executes asynchronously — once they are called, they are pushed to Javascript's event loop. However the await operator inside the async function makes Javascript to wait. Once "waiting" is finished, the code following that in the async function is executed.
Understanding Event-Loop, Asynchronous, Single-Threaded in Javascript is a nice video explaining these terms.