How can I improve this REST request promise code? - javascript

I'm trying to improve the use of promises in some code that retrieves REST resources. I have a number of REST calls that make the perform the same sequence of actions:
Fetch the config resource from the server if it hasn't been obtained previously
Dispatch a flux action indicating the start of the request
Send a request for the actual resource
Parse the JSON in the response
Dispatch a flux action indicating success with the parsed data.
The code I currently use to do this is below.
getThingsFromServer() {
return getConfigIfNeeded().then(() => {
dispatchStartOfRequestAction();
return window.fetch(`${store.baseURL}/resource`)
.then((response) => {
if(response.ok) {
return response.json();
} else {
return Promise.reject(new Error(`${response.status} ${response.statusText}`));
}
}, (error) => {
return Promise.reject(new Error(`Network error: ${error.message}`));
})
.then((data) => {
dispatchSuccessAction(data);
}, (error) => {
return Promise.reject(new Error(`JSON parse error: ${error.message}`));
})
.catch((error) => {
dispatchFailureAction(error)
});
});
}
There are a number of error conditions I would like to be able to handle individually, after which I want to dispatch a failure action (which is done in the catch()).
At the moment, if one of the individual then() error handlers gets called, every subsequent then() error handler is also called before finally calling the catch(). I want just one individual handler and the catch to be called.
I could dispense with handling each error individually and use a single catch at the end of it all but various sources both support and vilify the practice of handling all these different errors in the same way at the end of a promise chain. Is there a "right" answer to this beyond personal opinion?

if one of the individual then() error handlers gets called, every subsequent then() error handler is also called
Yes, if you throw (or return a rejected promise) from then handler, the promise will get rejected and subsequent error handlers will get called. There's really no way around this. To differentiate the errors, you'll have to nest (see also here).
In your case, you'll want to use
dispatchStartOfRequestAction();
return fetch(`${store.baseURL}/resource`)
.then(response => {
if (response.ok) {
return response.json()
.catch(error => {
throw new Error(`JSON parse error: ${error.message}`);
});
} else {
throw new Error(`${response.status} ${response.statusText}`);
}
}, error => {
throw new Error(`Network error: ${error.message}`);
})
.then(dispatchSuccessAction, dispatchFailureAction);

Related

Handling exceptions in Vuex mutators

I came across this scenario today, and would like to know the best approach to handling it.
The Vuex action uses Axios to post a record to the back end, catching any exceptions. Once the POST request returns, a mutator is called with the returned data. The issue is if an exception occurs in the mutator, it is not caught by error handling in the action, or the calling component.
Example
If the spread operator within the mutation is removed, an "object expected, got array" exception would be thrown, which I would expect to be caught within the .catch() function of the action call within the component, but it is not. Why and how can this be handled? If the solution to wrap the action call in an additional try/catch?
Action
async saveRecords({ commit }, records) {
return new Promise((resolve, reject) => {
axios.post("/records", records)
.then((res) => {
commit('addRecords', res.data.records)
resolve();
})
.catch((error) => {
reject(error);
})
})
}
Mutation
addCase: function (state, caseRecord) {
console.log("inside mutation")
state.cases.push(...caseRecord)
},
Component
this.saveRecords(records)
.catch((e) => {
// IExpecting exception from mutation to be caught here
})
});

With Javascript Promises, are there best practices regarding the use of "error" versus catch clauses?

In learning to write in JavaScript with Promises, I'm encountering two different ways of dealing with errors. One is to use a catch, as in this example:
axios.post('/someapi', {
somedata1: 'foo'
})
.then((result) => {
console.log(result);
})
.catch((exception) => {
console.log(exception);
});
The other is to have a clause in the then for the rejected case:
axios.post('/someapi', {
somedata1: 'foo',
})
.then((response) => {
console.log(response);
}, (error) => {
console.log(error);
});
Some authors seem to use one approach, other authors use the other, and it's not clear to me why. Are there situations in which it's necessary or desirable to use one or the other?
(The example above uses axios, but that's just for the purposes of providing a code example. This question is not about axios.)
With Javascript Promises, are there best practices regarding the use of “error” versus catch clauses?
There is no universal "best practice" for that question because it depends upon what specific behavior you want your code to have. As others have mentioned, you get a different behavior in a few ways.
Some authors seem to use one approach, other authors use the other, and it's not clear to me why. Are there situations in which it's necessary or desirable to use one or the other?
Yes, there are situations to use one or the other. They provide potentially different behaviors. Only if you're 200% sure that your successHandler in .then(successHandler) can never throw or return a rejected promise, would there be no meaningful difference.
As a summary:
When using p.then(successHandler).catch(errorHandler), errorHandler will get errors that occur either from a rejected p or from an error or rejection from the successHandler.
When using p.then(successHandler, errorHandler), errorHandler will be called from a rejection of p and will NOT get called from an error or rejection from the successHandler.
Different behaviors that are useful for different circumstances.
In this .catch() example below, the .catch() will catch an error that occurs (either accidentally from a coding error, a thrown exception or when returning some other promise that rejects).
Promise.resolve("hello").then(greeting => {
console.log("throwing error");
throw new Error("My .then() handler had an error");
}).catch(err => {
// will get here
console.log("Caught error in .catch()\nError was: ", err.message);
});
But, when using the second argument to .then(), that error from the .then() handler will not be caught:
Promise.resolve("hello").then(greeting => {
console.log("throwing error");
throw new Error("My .then() handler had an error");
}, err => {
// won't get here
console.log("Caught error in .catch()\nError was: ", err.message);
});
So, sometimes you want an error that might occur in the .then() handler to hit this immediate error handler and sometimes you don't want it to hit that error handler because you want that error handler to only process errors from the original promise and you have some other catch handler later in the promise chain that will deal with this error.
Recommendation
In general, I would advise that you start out with the .catch() handler because it catches more errors and does not require that there be some other .catch() handler elsewhere in the promise chain in order to be safe. Then, you switch to the .then(successHandler, errorHandler) form if you explicitly don't want this errorHandlerto be called if there's another error in the successHandler AND you have somewhere else in the promise chain where an error in the successHandler would get caught or handled.
Both the syntaxes work out of the box. But the first one has an advantage that the Catch block is able to catch an error thrown by the Promise Rejection as well as an error thrown by then block.
Here is the Example you can try in Node and you will have a better idea about it.
function x () {
return new Promise((resolve, reject) => {
return resolve('Done...');
});
}
x()
.then(response => {
console.log('RESPONSE---', response)
throw 'Oops...Error occurred...';
})
.catch(error => console.log('ERROR---', error));
x()
.then(
response => {
console.log('RESPONSE---', response)
throw 'Oops...Error occurred...';
},
error => console.log('ERROR---', error)
);

What is the correct way to check errors with firebase auth methods?

I recently started using firebase. I am creating a simple app containing email password authentication. When the user submits the form there are two possibilities.
If there is any error then Form will show error will remain visible.
If there is no error Form will hide and remove error.
The loader needs to be hide after processing in either case. The relevant part of the code is.
let error = false;
auth
.createUserWithEmailAndPassword(email, password)
.catch(err => {
if (err) error = true;
RegisterForm.setError(err.message);
})
.then(x => {
if (!error) {
RegisterFormDialog.close();
}
})
.finally(() => {
MainLoader.hide();
});
The above code is working completely fine but the problem is that I have to create an extra variable error is the outer scope and then check it inside then(). Is there any callback what will only run if there are no errors. Because then() is called even when errors are there.
The usual practice for all methods that return a promise (not just Firebase) is to put then before catch when handling the results:
let error = false;
auth
.createUserWithEmailAndPassword(email, password)
.then(x => {
RegisterFormDialog.close();
})
.catch(err => {
if (err) error = true;
RegisterForm.setError(err.message);
})
.finally(() => {
MainLoader.hide();
});
If the promise resolves successfully, the then callback will be invoked. Otherwise, the catch callback will be invoked. But never both.

What results can i expect from nesting a "then.catch" inside another "then" in javascript?

Right now I am working with code resembling a structure like this:
doThis().then(() => {
doThat().then([...]).catch(e => {
console.log('internal catch');
});
}).catch(e => {
console.log('external catch');
});
If the interior function doThat() ends up returning an exception, will the internal catch, the external catch, or both send messages?
In your case, only the internal catch will handle the error thrown by the doThat function call. The external doThis function won't be aware of what's going on at all. If you want the external catch to handle it, then you'll have to reorganise your code a bit.
doThis().then(() => {
// By returning the internal promise, the external one will
// now be able to handle both success and error.
return doThat().then([...]).catch(e => {
console.log('internal catch');
// Rethrowing will ensure that the error will propagate to the external handler.
// Otherwise, handling the error in the catch function will make
// the promise successful again.
throw e
});
}).catch(e => {
console.log('external catch');
});

Call for two method in promise chain

I've the following promise which work well
troces.run(value, "../logs/env.txt")
.then(function (data) {
console.log(data);
return updadeUser(val, arg, args[1])
// Now here I need to add new method updateAddress(host,port,addr)
}).catch(function (err) {
console.error(err);
});
Now I need to add additional method call inside the the first .then
that the update user and the updateAddress will work together
My question are
assume that updateUser will need to start 10 ms after the update
address How is it recommended to do so?
in aspects of error handling if one of the process failed (send error message ) I Need to exit (process.exit(1);)
Use .all:
troces.run(value, "../logs/env.txt")
.then(data => {
console.log(data);
return Promise.all([updadeUser(val, arg, args[1]),
updateAddress(host,port,addr)]);
}); // no need to add catches bluebird will log errors automatically
If you really need the 10ms delay, you can do:
troces.run(value, "../logs/env.txt")
.then(data => {
console.log(data);
return Promise.all([updadeUser(val, arg, args[1]),
Promise.delay(10).then(x => updateAddress(host,port,addr))]);
}); // no need to add catches bluebird will log errors automatically
Although I suspect that you really just want updateUser to happen before updateAddress which can be easily solved with:
troces.run(value, "../logs/env.txt")
.then(data => {
console.log(data);
return updadeUser(val, arg, args[1]).then(_ => updateAddress(host,port,addr));
}); // no need to add catches bluebird will log errors automatically
If you need to exit on promise error, you can do:
process.on("unhandledRejection", () => process.exit(1));
Although I warmly recommend you create meaningful error messages, just a non-zero process exit code is hard to debug.

Categories

Resources