Making Synchronous HTTP Requests in Node.js

nodejs
Published on May 21, 2019

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.

In this Tutorial