Javascript Promise, why is reject unhandeled? - javascript

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.

Related

Javascript How do I deal with errors in Promise and makes the loop keepgoing and save the file?

I'm trying to loop through the list of files and eventually save the array in my local drive.
However, the problem is once my code confronts any error, it stops running and doesn't save anything. What I want to achieve is to keep my loop running even if there is an error.
I'm still not fully confident with using Promise,
I'm going to fetch information using below code.
function getSongs(id, number) {
return new Promise((res, rej) => {
geniusClient.getArtistSongs(id, { "page": `${number}`, "per_page": "50" }, (error, songs) => {
// console.log(error);
// console.log(JSON.parse(songs).response.songs);
if (error) {
res('error', 'id is: ', id);
} else {
let songsArray = JSON.parse(songs)
// console.log(songsArray.response.songs)
res(songsArray.response.songs);
}
})
})
}
and save the songs once I fetch all of them as below.
for (artist of resultArray) {
console.log(artist.id);
let songArray = await getSongs(artist.id, 1);
artist.songs.push(...songArray)
}
// for (artist of resultArray) {
// console.log(artist.id);
// let songArray = await getSongs(artist.id, 2);
// artist.songs.push(...songArray)
// }
roundnumber++;
console.log('round number is', roundnumber);
fs.writeFileSync('./songTest/test.json', JSON.stringify(resultArray))
Suggested approach ...
Make sure that getSongs() returns a rejected Promise for as many error cases as possible.
function getSongs(id, number) {
return new Promise((res, rej) => {
// a synchronous error here will result in Promise rejection.
geniusClient.getArtistSongs(id, { "page": `${number}`, "per_page": "50" }, (error, songs) => {
try {
if (error) {
throw error; // will be caught below and result in Promise rejection.
} else {
// an (unexpected) error thrown here will also be caught below and result in Promise rejection.
let songsArray = JSON.parse(songs);
res(songsArray.response.songs);
}
} catch(error) {
rej(error); // reject (expected/unexpected error)
}
});
});
}
In the caller code, add a try/catch structure to handle errors.
for (let artist of resultArray) {
// ^^^ don't forget to declare variables
try {
let songArray = await getSongs(artist.id, 1);
artist.songs.push(...songArray);
} catch(error) {
// catch any error arising from the try block,
// including any arising from Promise rejection in getSongs().
artist.songs.push({ 'error': error.message, 'id': artist.id }); // or whatever you want to represent an error
}
}
You could use Promise.allSettled. From the MDN docs
The Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.
Store the promises in an array, don't await them and pass that array to Promise.allSettled.
With this all your errors (if any) will be stored and returned to you in array at the end of the operation.

Promises: some problems with reject

I have an issue with promise in 'Angular 2'.
Please see my code below:
getPromise1().then((result) => {
console.log("promise1 result");
}, (error) => {
console.log("promise1 error");
});
function getPromise1() {
return getPromise2().then((result) => {
console.log("promise2 result");
}, (error) => {
console.log("promise2 error");
});
}
function getPromise2() {
return new Promise((resolve, reject) => {
reject("error");
});
}
And the result is: promise2 error and promise1 result.
I don't know why not promise2 error and promise1 error
Any problem and the solution for this case?
This is preview link: http://plnkr.co/edit/RTc1wYfO8e1YPUrXM6GN
When a promise rejects, the control jumps to the closest rejection handler down the chain.
so, Here the .catch block finishes normally. So the next successful handler is called. Or it could return something, that would be the same.
Hence the result
And the result is: promise2 error and promise1 result.
So you could have as many .then as we want, and then use a single .catch at the end to handle errors in all of them.
But to get the following result
I don't know why not promise2 error and promise1 error
you need to re-throw the error
throw error;
And here the .catch block analyzes the error and throws it again:
function getPromise1() {
return getPromise2().then((result) => {
console.log("promise2 result");
}, (error) => {
console.log("promise2 error");
throw error;
});
}
If you handle a rejection in any promise chain then that's going to make the resulting chain to be fulfilled until an error is found then it goes back to being rejected. Think about it in terms of sync code, would you have expected this to print error A and error B?
function myFnA(){
throw new Error('A is bad');
}
function myFnB(){
try {
myFnA();
console.log('good A');
} catch(e){
console.log('error A');
}
}
function myFnC(){
try {
myFnB();
console.log('good B');
}
catch(e){
console.log('error B');
}
}
myFnC();
myFnB fixes myFnA error. So myFnB is doing it's job right.

promise chain when rejected but only have then implermented

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.

Promise.all find which promise rejected

In my code, I am using Promise.all() to run code asynchronously once some promises have all fulfilled. Sometimes, one promise will fail, and I'm not sure why. I would like to know which promise is failing. Passing a callback as a second parameter to the .then method does not help much, as I know that a promise is rejecting but not which promise is rejecting.
A stack trace does not help either, as the first item is the Promise.all()'s error handler. A line number from the Error object passed to the first parameter of the second function passed to the try function of the Promise.all() is simply the line number of the line where I log the line number.
Does anybody know of a way to find out which promise is rejecting?
You can use an onreject handler on each promise:
Promise.all(promises.map((promise, i) =>
promise.catch(err => {
err.index = i;
throw err;
});
)).then(results => {
console.log("everything worked fine, I got ", results);
}, err => {
console.error("promise No "+err.index+" failed with ", err);
});
In general, every rejection reason should contain enough information to identify the issue that you need to handle (or log).
this wrapper will wait for and return every result and/or rejection
the returned array will be objects
{ // this one resolved
ok: true,
value: 'original resolved value'
},
{ // this one rejected
ok: false,
error: 'original rejected value'
},
{ // this one resolved
ok: true,
value: 'original resolved value'
},
// etc etc
One caveat: this will wait for ALL promises to resolve or reject, not reject as soon as the first rejection occurs
let allresults = function(arr) {
return Promise.all(arr.map(item => (typeof item.then == 'function' ? item.then : Promsie.resolve(item))(value => ({value, ok:true}), error => ({error, ok:false}))));
}
allresults(arrayOfPromises)
.then(results => {
results.forEach(result => {
if(result.ok) {
//good
doThingsWith(result.value);
} else {
// bad
reportError(result.error);
}
});
});

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