promise chain when rejected but only have then implermented - javascript

I was reading stuffs about promise and fetch and got really confused. I got the following code from Introduction to fetch.
My question is: what happens if status returns a rejected promise? then(json) is chained after then(status), does that mean then(json) won't do anything since then(json) only gets executed when status returnes a resolved promise? Or does that mean the chain just keeps passing all the thens if status returns rejected promise until it reaches reach catch at the bottom, and the catch catches error?
Or if I was wrong, what is the correct interpretation of this code?
function status(response) {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
} else {
return Promise.reject(new Error(response.statusText))
}
}
function json(response) {
return response.json()
}
fetch('users.json')
.then(status)
.then(json)
.then(function(data) {
console.log('Request succeeded with JSON response', data);
}).catch(function(error) {
console.log('Request failed', error);
});

In my early days of trying to understand promises, I thought of the .then chain as being two chains ... success and rejection
a rejection or error causes the "execution" to "jump" from success to rejection
if a rejection handler returns a value that isn't a rejected promised, the "execution" will "jump" to the success chain
Note: my earliest exposure to promises had no .catch ... because .then actually accepts two arguments onFullfilled and onRejected - if either of those is not a function it is ignored -
So, your code can be written as follows:
function status(response) {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
} else {
return Promise.reject(new Error(response.statusText))
}
}
function json(response) {
return response.json()
}
function log(data) {
console.log(data);
}
function handleError(error) {
console.log('Request failed', error);
}
fetch('users.json')
.then(status, null)
.then(json, null)
.then(log, null)
.then(null, handleError);
Now it's fairly clear that given an error in function stats, the "on rejected" chain is in "effect" (I really need to think of better terminology), and there's nothing in the reject chain until the very bottom, therefore, that's the next code that gets executed
Note .catch in some Promise libraries is simply the following
Promise.prototype.catch = function catch(onRejected) {
return this.then(null, onRejected);
};

When a promise gets rejected it goes directly to the .catch() without executing anything else of the chain.
So if status returns a Promise.reject only the catch function will execute.

Related

Nested promise not throwing error in parent catch block

My code :
return resultSet.reduce (prev,next)=> {
return function1 ({
}).then(response => {
console.log('suscess' + someID)
return functionA (response.someID)
}).catch (err => {
console.log('failed' + someID)
return functionB (response.someID)
})
}, promise.resolve());
function1: function (){
return function2_Credentials().then{
return function3_insertSqlRecord().then{
return function4_appSearch().then (function(response){
return function5().then (return response)
.catch (function (error){
throw Error(error);
});
});
}.catch (function(error){
throw Error(error);
});
}
}.catch (function(error){
throw Error(error);
});
Inside my nested promise, if any of the inner functions failed I can get the error in catch block but it is not available function1 catch block. For example If i get function4_appSearch fails and return any error that error is accessible to the last catch block but not carried away until the function1's catch block. why am I not getting the error at console.log('failed' + someID) ? and how do I get access of error from child promise untill to the parent pormise's catch block?
Your current code isn't valid; it has multiple syntax errors, and everything ever passed to a .then needs to be a Promise. Instead of
return function3_insertSqlRecord().then{
return function4_appSearch().then
you should do something like
return function3_insertSqlRecord()
.then(function4_appSearch)
.then( // etc
This keeps your code flat and ensures that that the Promises each function generates are chained properly.
If you want the caller of function1 to handle the error, then just let the error propagate up the Promise chain - don't .catch inside function1, because whenever you .catch, a resolved Promise results (but you want the caller to be able to see a rejected Promise). You can explicitly throw inside the .catch to make the Promise reject, but if that's all you're doing inside the .catch, there isn't really any point.
Try this instead:
function function1() {
return function2_Credentials()
.then(function3_insertSqlRecord)
.then(function4_appSearch)
.then(function5);
}
There also isn't any point to using reduce if you aren't going to use the accumulator (here, prev) or the item in the array you're iterating over (next).

How to fix stop promise returned in .catch beeing processed in the .then block of the preceding promise. Ie .catch should stay in .catch

Then a promise calls another promise and the inner promise returns from catch the outer one processes in the .then block
I have searched here and google generally.
Tried to use a simple try.. catch. But will not work with calling a promise
assignResolver(data)
.then(function(resp) {
console.log("map then");
console.log(resp);
})
.catch(function(err) {
console.log("map catch");
console.log(err);
});
export async function assignResolver(data) {
csrf();
return api
.post("/api/task/assignResolver", data)
.then(function(res){
console.log("in api then block");
return res.data;
} )
.catch(function(err) {
console.log("in api then block");
console.log(err);
});
}
Just throw the error again, when inside the inner catch, and it'll be handled in the outer catch, and not the outer .then:
export async function assignResolver(data) {
csrf();
return api
.post("/api/task/assignResolver", data)
.then(function(res){
console.log("in api then block");
return res.data;
} )
.catch(function(err) {
console.log("in api then block");
console.log(err);
throw err;
});
}
But this is a bit strange to do - it usually makes more sense to catch in only one place. For example, if assignResolver really needs to be able to do something specific when encountering an error, and your outer caller needs to be able to do something else when encountering the error as well, having two catches can be an option, but in most cases, you can just have a single catch wherever the error can be handled properly.
Here, unless assignResolver itself needs to do something on encountering the error, leave out its catch entirely:
export async function assignResolver(data) {
csrf();
return api
.post("/api/task/assignResolver", data)
.then(function(res){
console.log("in api then block");
return res.data;
})
}
.catch is meant to handle the error, so afterwards the promise chain will continue regularily with the next .then. To continue with the next .catch you have to rethrow the error from inside the .catch or return a rejected promise.

Javascript Promise, why is reject unhandeled?

I have the following code:
function validateRole(message, cmdCalled) {
return new Promise(function(resolve, reject) {
winston.debug('Checking user\'s role');
Game.findOne({
running: true,
'players.id': message.author.id
}, {
'players.$': 1
}).then(function(result) {
console.log(result);
if (!result) reject('playerNotFound');
if (cmdCalled === 'vote' && result.players[0].role.match(/wolf/gi)) {
message.author.send('Wolves are not allowed to vote!');
reject('voteDenied');
}
}, reject).catch(function(err) {
winston.debug(err);
});
});
}
validateRole(message,'someCommand').then(...)
If any of the conditional statements fail I get error: Unhandled Rejection at: Promise Promise { <rejected> 'voteDenied' } reason: voteDenied. Why is this not handled by caught? Or is this the wrong way to handle things, would it be better to just resolve with a false value or something and handle result in then() function?
because you }, reject). the returned promise. and as reject returnes undefined, there's no way that .catch is executed.
+don't use the Promise-constructor antipattern.
Let's clean this up a bit, and you add logging wherever your want.
function validateRole(message, cmdCalled) {
return Game.findOne({
running: true,
'players.id': message.author.id
}, {
'players.$': 1
})
.catch(function(err){
console.log("error", err);
//implicitely returns undefined as the `result` into the following `.then()`
})
.then(function(result) {
if (!result)
throw 'playerNotFound';
if (cmdCalled === 'vote' && result.players[0].role.match(/wolf/gi)) {
message.author.send('Wolves are not allowed to vote!');
throw 'voteDenied';
}
});
//and after this you either have a promise with a valid result,
//or a rejected promise containing 'playerNotFound' or `voteDenied`
}
or if you want to handle Errors with the request seperately
function validateRole(message, cmdCalled) {
return Game.findOne({
running: true,
'players.id': message.author.id
}, {
'players.$': 1
})
.then(function(result) {
if (!result)
throw 'playerNotFound';
if (cmdCalled === 'vote' && result.players[0].role.match(/wolf/gi)) {
message.author.send('Wolves are not allowed to vote!');
throw 'voteDenied';
}
}, function(err){
//this will catch Errors from `findOne()` but not the errors thrown in the function above
console.log(err);
throw 'requestFailed';
});
//and after this you either have a promise with a valid result,
//or a rejected promise containing 'playerNotFound' or `voteDenied` or `requestFailed`
}
At my sight most interesting part of the code is not shown and replaced with ... into your example.
From your error message is can be seen that validateRole returns Promise and you're dealing with it at last line of example code. If your validateRole().then() contains no handler for promise rejection (and it can't be seen from your example) then this error is obvious - you're not handling promise rejection.
So please either check that you have handler for promise rejection into hidden part of your example or show what do you have here.

Rethrowing error in promise catch

I found the following code in a tutorial:
promise.then(function(result){
//some code
}).catch(function(error) {
throw(error);
});
I'm a bit confused: does the catch call accomplish anything? It seems to me that it doesn't have any effect, since it simply throws the same error that was caught. I base this on how a regular try/catch works.
There is no point to a naked catch and throw as you show. It does not do anything useful except add code and slow execution. So, if you're going to .catch() and rethrow, there should be something you want to do in the .catch(), otherwise you should just remove the .catch() entirely.
The usual point for that general structure is when you want to execute something in the .catch() such as log the error or clean up some state (like close files), but you want the promise chain to continue as rejected.
promise.then(function(result){
//some code
}).catch(function(error) {
// log and rethrow
console.log(error);
throw error;
});
In a tutorial, it may be there just to show people where they can catch errors or to teach the concept of handling the error, then rethrowing it.
Some of the useful reasons for catching and rethrowing are as follows:
You want to log the error, but keep the promise chain as rejected.
You want to turn the error into some other error (often for easier error processing at the end of the chain). In this case, you would rethrow a different error.
You want to do a bunch of processing before the promise chain continues (such as close/free resources) but you want the promise chain to stay rejected.
You want a spot to place a breakpoint for the debugger at this point in the promise chain if there's a failure.
You want to handle a specific error or set of errors, but rethrow others so that they propagate back to the caller.
But, a plain catch and rethrow of the same error with no other code in the catch handler doesn't do anything useful for normal running of the code.
Both .then() and .catch() methods return Promises, and if you throw an Exception in either handler, the returned promise is rejected and the Exception will be caught in the next reject handler.
In the following code, we throw an exception in the first .catch(), which is caught in the second .catch() :
new Promise((resolve, reject) => {
console.log('Initial');
resolve();
})
.then(() => {
throw new Error('Something failed');
console.log('Do this'); // Never reached
})
.catch(() => {
console.log('Something failed');
throw new Error('Something failed again');
})
.catch((error) => {
console.log('Final error : ', error.message);
});
The second .catch() returns a Promised that is fulfilled, the .then() handler can be called :
new Promise((resolve, reject) => {
console.log('Initial');
resolve();
})
.then(() => {
throw new Error('Something failed');
console.log('Do this'); // Never reached
})
.catch(() => {
console.log('Something failed');
throw new Error('Something failed again');
})
.catch((error) => {
console.log('Final error : ', error.message);
})
.then(() => {
console.log('Show this message whatever happened before');
});
Useful reference : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#Chaining_after_a_catch
Hope this helps!
There is no important difference if you leave out the catch method call completely.
The only thing it adds is an extra microtask, which in practice means you'll notice the rejection of the promise later than is the case for a promise that fails without the catch clause.
The next snippet demonstrates this:
var p;
// Case 1: with catch
p = Promise.reject('my error 1')
.catch(function(error) {
throw(error);
});
p.catch( error => console.log(error) );
// Case 2: without catch
p = Promise.reject('my error 2');
p.catch( error => console.log(error) );
Note how the second rejection is reported before the first. That is about the only difference.
So it sounds like your question is, "In the promise chain, what does the .catch() method do?"
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
The throw statement "will stop (the statements after throw won't be executed), and control will be passed to the first catch block in the call stack. If no catch block exists among caller functions, the program will terminate."
In the promise chain, the .then() method will return some type of data chunk. This return of the chunk will complete the promise. The successful return of the data completes the promise. You can think of the .catch() method in the same way. .catch() however will handle unsuccessful data retrieves. The throw statement completes the promise. Occasionally, you will see developers use .catch((err) => {console.log(err))} which would also complete the promise chain.
You actually don't need to re throw it, just leave the Promise.catch empty otherwise it will consider as un handle the reject and then wrap the code in a try catch and it will catch the error automatically which is passing down.
try{
promise.then(function(result){
//some code
}).catch(function(error) {
//no need for re throwing or any coding. but leave this as this otherwise it will consider as un handled
});
}catch(e){
console.log(e);
//error can handle in here
}
In the promise chain, it is better to use .catch
ex in function f2: .then(...).catch(e => reject(e));
test1 - with try catch
test2 - without try or .catch
test3 - with .catch
function f1() {
return new Promise((resolve, reject) => {
throw new Error('test');
});
}
function f2() {
return new Promise((resolve, reject) => {
f1().then(value => {
console.log('f1 ok ???');
}).catch(e => reject(e));
});
}
function test1() {
console.log('test1 - with try catch - look in F12');
try {
f2().then(() => { // Uncaught (in promise) Error: test
console.log('???'); });
} catch (e) {
console.log('this error dont catched');
}
}
function test2() {
console.log('test2 - without try or .catch - look in F12');
f2(); // Uncaught (in promise) Error: test
}
function test3() {
console.log('test3 - with .catch');
f2().then(value => {
console.log('??');
}).catch(e => {
console.log(' now its ok, error ', e);
})
}
setTimeout(() => { test1();
setTimeout(() => { test2();
setTimeout(() => { test3();
}, 100);
}, 100);
}, 100);

Promise still go to the next chain after I return res.end()

I'm using bluebird in node with express ,when I return res.end(),Promise still goes to the next chain,here's my code:
Promise.resolve(operate).then(function(data){
if (!!data && data.length !=0) {
log.info('success');
return true;
}else {
log.warn('fail');
return res.end();
}
}).then(function(data){
if(data) {
log.info('done');
return res.end();
}else {
log.warn('fail');
return res.end();
}
})
And I got 2 'fail' in the log if it's failed,how can i make it stop if i got 'fail' in the first time?Thank you!
bluebird version:~2.10.2;
node: 4.3.1;
express:~4.2.0
You can return either an error or a rejected promise instead of res.end. This will skip all success handlers until the end of the promise chain (technically until the first rejection handler):
Promise.resolve()
.then(() => Promise.reject())
.then(
() => console.log('this does not run'), // <-- success handler
() => console.log('this runs') // <-- rejection handler
);
So in your code:
Promise.resolve(operate).then(function(data){
if (!!data && data.length !=0) {
log.info('success');
return true;
} else {
log.warn('fail');
res.end();
return Promise.reject(/* optionally pass an error */); // <--- return rejected promise
}
}).then(function(data){
// this won't run if previous promise rejects
})
By definition the then method always return a Promise, so the chain is going to continue.
As it has been already pointed out you could reject the promise instead and catch it afterwards. If the error handling is the same in all cases you can use a catch at the end of the chain (putting a catch at the end is considered a good practice anyway).
Promise.resolve(operate).then(function(data){
if (!!data && data.length !=0) {
log.info('success');
return true;
} else {
return Promise.reject('fail');
}
})
.then(function(data) {
// true is being returned above so it will enter here always
// I guessed something instead of true could come here
if(data) {
log.info('done');
return res.end();
} else {
return Promise.reject('fail');
}
})
.catch(function(err) {
log.warn(err);
return res.end();
});
In this case returing a rejected Promise seems legit, but depending on the situation returning a rejected Promise to escape of the Promise chain could not be recommended because it can be mixed with a real rejection. In those case restructuring your chain could be the best option.

Categories

Resources