Sunday 26 December 2021

Promises and catch

I've recently been working on JavaScript code written by other people where they were using .then()/.catch() chains rathern than async-await magic, and I should not change that, so I've had to review a bit how then()/.catch() works (thanks to async-await I've rarely used them in the last years).

The first point is a reminder about promises rejection in general. When inside .then() we want to reject the promise, should we use Promise.reject(new Error()) or throw new Error()?
Well, it's basically the same. If you do a throw inside a .then or a .catch the .then or .catch will trap the exception and reject the current promise. You can read more about this here.

Both methods produce the exact same response. The .then() handler catches the thrown exception and turns it into a rejected promise automatically.

The second point is that I had almost forgottent that the .then() method accepts a second parameter, a handler function to be called when the promise has been rejected. So, what's the difference between that and chaining a .catch() call? I mean:



getData()
.then(
	data => {/* do whatever */},
	error => {/* treat error */}
);

vs

getData()
.then(data => {/* do whatever */})
.catch(error => {/* treat error */});



Well, the behaviour is almost the same, but there's a subtle difference. If the success handler throws an error, in the first case, that error is not being caught, but in the second case, the .catch, will catch that error. It's well explained here

The next thing is with how many .catch() calls you need. If you have a chain of .then() calls and want to catch any exception in any of them, and apply the same treatment, you need just one .catch() at the end of the call. This means that this code:


async function getProcessedMessage(){
	try{
		let msg = await getMessage();
		msg = await correctOrtography(msg);
		msg = await replaceOddWords(msg);
		msg = await addExtraFormatting(msg);
		return msg;
	}
	catch(error){
		console.log("unable to perform the whole processing");
		return "";
	}
}

is equivalent to this:


function getProcessedMessage(){
	return getMessage()
	.then(correctOrtography)
	.then(replaceOddWords)
	.then(addExtraFormatting)
	.catch(error => {
		console.log("unable to perform the whole processing");
		return "";
	})
}


If what we want is managing exceptions call by call (I mean, differently for different .then() calls), to do some treatment (logging the exception and throwing a "higher level one") or recovering (we replace by a default value and continue with the ensuing calls) then we'll have a .catch() for each .then(). This code:


async function getProcessedMessage(){
	let msg;
	try{
		msg = await getMessage();
	}
	catch(error){
		console.log("failed to retrieve message, we'll use the default");
		msg = "default";
	}
	try {
		msg = await translate(msg);
	}
	catch(error){
		console.log("low level error: " + error.message);
		throw new Error("failed to translate);		
	}
	return msg;
}

is equivalent to this:


function getProcessedMessage(){
	getMessage().
	.catch(error){
		console.log("failed to retrieve message, we'll use the default");
		return "default";
	}
	.then(translate)
	.catch(error){
		console.log("low level error: " + error.message);
		throw new Error("failed to translate);			
	}
}

No comments:

Post a Comment