How i prevent calling nested then method? - javascript

Basically, I have login api and nested then function is there, my question is how I stop from calling as certain condition, pasting my code:
login(values.email, values.password)
.then((res) => {
if (res?.value?.data?.unverified) {
console.log('>>> in', res, RESEND_VERIFICATION_EMAIL_DIALOG);
} else getMe(true);
})
.then(() => ...)
So, if it comes in the above if I have to stop calling nested then and all code.
Any help would be appreciated, thanks in advance :).

you'd better use .then, .catch
login(values.email, values.password)
.then((res) => {
if (res?.value?.data?.unverified) {
console.log('>>> in', res, RESEND_VERIFICATION_EMAIL_DIALOG);
} else getMe(true);
}).catch(function(e) {
console.log(e); // "oh, no!"
});
PS: I found a good resource about stopping the promise chain which I hope would help you

Related

Firebase Call still executing after sending res.status.send()

I have the following in the my code. I'm trying to prevent a user from reviewing someone twice so therefore it should stop executing code in the IF statement and end firebase calls.
However, it continues executing the rest of the ".then" statements. I tried changing it to end() but that didn't work either. What am I doing wrong?
exports.reviewUser = (req, res) => {
db.collection("users")
.doc("user")
.get()
.then((doc) => {
if (doc.exists) {
return res
.status(400)
.json({ error: "You cannot review someone twice." })
.send();
}
})
.then(() => {
//Additional functions
})
.then(() => {
//Additional functions
})
}
Update:
I followed the answer and here's my edited code that works for future reference.
exports.reviewUser = async(req, res) => {
let existingReview = await checkReviewExists(userid);
if (existingReview) {
return res
.status(400)
.json({ error: "You cannot review someone twice." })
.end();
}
db.collection("users")
.doc("user")
.then(() => {
//Additional functions
})
.then(() => {
//Additional functions
})
}
function checkReviewExists(id1) {
return db
db.collection("users")
.doc(id1)
.get()
.then((doc) => {
if (doc.exists) {
console.log("cannot review twice");
return true;
}
else
return false;
});
}
As per the Firebase doc, we could end HTTP function with send(), redirect(), or end(). However, in your case you are resolving the firestore Promise with multiple .then(). So here, though the first one returns, it goes to the second then()
It's called Promises chaining
The above reference gives detailed info with examples. I prefer to go with one .then() or simply async / await would do the work.

Testing function throws UnhandledPromiseRejectionWarning. Causing test not to pass

I played with Mocha Testing.
I have noticed that my function throws Warring of "UnhandledPromiseRejectionWarning" in few places. It makes the script to do not pass the check.
I could not find useful example on the internet that will teach/show a good way to fix the problem. Maybe one of you can help.
If you have any other comments to my code please feel free to share. I am here to learn :)
Function where the problem occurs.
it('/POST /logout => Logout a user by purging a session', (done) => {
let loginInfo = {};
loginInfo.usr = 'testuser';
loginInfo.psw = 'mochatesting197';
let agent = chai.request.agent(app);
let json = {};
json.logout = true;
agent.post('/login')
.send(loginInfo)
.then((res) => {
return agent.post('/logout')
.send(json)
.then((res) => {
res.should.have.status(200);
res.body.should.be.a('object');
res.body['success'].should.have.property('message').eql('YOU HAVE LOGGED OUT');
done();
}).catch(function (err) {
throw err;
});
});
});
An UnhandledPromiseRejectionWarning occurs when a Promise is rejected but does not have a catch handler associated with it. Since a handler can be attached to a Promise at any time (even after it is rejected), the default behaviour is to log a warning to the default out (console) after a number of event loop turns.
In the code you supply, the most likely cause is that your catch block is in the wrong place. Try moving the catch handler to the bottom of your Promise chain.
This won't definitely solve the problem, but it's the most likely place within the code you provided. Also, note that when using the 'done' callback mechanism from Mocha, you shouldn't throw. Instead, you should call done with an error (also shown below)
it('/POST /logout => Logout a user by purging a session', (done) => {
let loginInfo = {};
loginInfo.usr = 'testuser';
loginInfo.psw = 'mochatesting197';
let agent = chai.request.agent(app);
let json = {};
json.logout = true;
agent.post('/login')
.send(loginInfo)
.then((res) => {
return agent.post('/logout')
.send(json)
.then((res) => {
res.should.have.status(200);
res.body.should.be.a('object');
res.body['success'].should.have.property('message').eql('YOU HAVE LOGGED OUT');
done();
})
})
.catch(function (err) {
done(err);
});
});

axios.all spread and catch all

I'm using .all method of popular library 'axios' for handling my ajax requests.
But how can I handle errors in case all requests got 404?
for example:
axios.all([
axios.get('http://some_url'),
axios.get('http://another_url'),
])
.then(axios.spread((someUrl, anotherUrl) => {
// ... boring stuff goes there
}))
.catch(() => {
//... error goes there
});
So, seems only one error has ben "catched".
How can I catch them all? Or maybe there any kinda .finally?
The problem (as you already know) is that you will get into catch block as soon as the first promise rejects, making it impossible to collect all failed responses in the same catch. However, you still can handle failed promises manually to aggregate errors and throw afterwards.
Check it this will work for you:
const promises = [
axios.get('http://some_url'),
axios.get('http://another_url'),
]
const promisesResolved = promises.map(promise => promise.catch(error => ({ error })))
function checkFailed (then) {
return function (responses) {
const someFailed = responses.some(response => response.error)
if (someFailed) {
throw responses
}
return then(responses)
}
}
axios.all(promisesResolved)
.then(checkFailed(([someUrl, anotherUrl]) => {
console.log('SUCCESS', someUrl, anotherUrl)
}))
.catch((err) => {
console.log('FAIL', err)
});
You will get into catch block if at least one of the promises fails. You can find one which one by checking err array of responses.
I don't think this is possible due to the fail fast behaviour of Promise.all. If any of your requests fail, they will automatically be the culprit and the result in the catch.
Promise.all([
Promise.reject(Error('1')),
Promise.reject(Error('2')),
Promise.reject(Error('3'))
]).then((results) => {
console.log(results)
}, (error) => {
console.log(error.message)
})
This resulting code will always print 1 as it is the first to fail.I think a similar feature was requested on the repo and they said it wasn't possible.
I was going to leave this as a comment but don't have a high enough reputation yet.
The solution from #dfsq did not work for me because it throws all requests when one has an error. I changed his code so every request either gets resolved or throws an error. #dfsq please review this answer if the code is correct, since I built it on your solution.
const promises = [
axios.get('http://some_url'),
axios.get('http://another_url'),
]
const promisesResolved = promises.map(promise => promise.catch(error => ({ error })))
function checkFailed (then) {
return function (responses) {
responses.forEach(response => {
if (response.error)
throw response;
return then(response);
})
}
}
axios.all(promisesResolved)
.then(checkFailed(response => {
console.log('SUCCESS', response)
}))
.catch((err) => {
console.log('FAIL', err)
});

How to call a function returning a promise on a result of a promise?

I have a basic mongoose authentication, with bcryptjs to hash passwords. Both bcrypt and mongoose return promises. In my routes.js I have the following script which gets stuck after finding the User in the db:
routes.post('/auth', (req, res)=> {
User.findOne({'local.username': req.body.username})
.then(
user=> Promise.all([user, user.validate(req.body.password)])
)
.then(
results => {
console.log(results);
res.json({token: jwt.sign({id: results[0]._id}, config.secret)});
}
)
.catch(
err=> console.log(err)
);
});
As you can see I find the user, and then try to call its validate method (which gets called), but it won't resolve the promise nor throw an error. In my user.js which defines my UserSchema I have this code to compare passwords:
UserSchema.methods.validate = function (password) {
return bcrypt.compare(password, this.local.password);
};
This is called, but the returned promise seems like it vanishes, it is not resolved, the results variable is never logged.
One more thing, if I edit user validation code to this:
UserSchema.methods.validate = function (password) {
return bcrypt.compare(password, this.local.password).then(
results => {
console.log(results)
}
)
};
I get true logged to console, so it must be working but I don't want to resolve my promise here, I want to attach the .then(...) in my router, isn't it possible?
What am I doing wrong?
UPDATE:
If I put the compare method in the routes.js it works, but that is not what I want to do, I want to keep it in the user.js, but I thought this might point out the problem which I still cannot see. I guess I have to call then() immediately on the promise, but I don't see why.
User.findOne({'local.username': req.body.username})
.then(
user=> Promise.all([user, bcrypt.compare(req.body.password,user.local.password)])
)
.then(
results => {
console.log(results);
res.json({token: jwt.sign({id: results[0]._id}, config.secret)});
}
)
.catch(
err=> console.log(err)
);
First of all why using Promise.all here? Especially i don't see the need for doing something like Promise.resolve(user). Without knowing how user.validate is working, i would write it like
routes.post('/auth', (req, res)=> {
let userId
User.findOne({'local.username': req.body.username})
.then(user => {
userId = user._id
return user.validate(req.body.password)
})
.then(results => {
console.log(results);
res.json({token: jwt.sign({id: userId}, config.secret)});
})
.catch(err => console.log(err))
});
I found out, that the problem is with mongoose. It wraps the module methods, and promises "get lost" somewhere. The solution to this is to use a sync compare method, or provide a callback.
Also I created an issue with this on github:
https://github.com/Automattic/mongoose/issues/4856
You are not doing anything with the Promise.all you call in the then.
Instead of
user=> Promise.all([user, user.validate(req.body.password)])
You should then it:
user.validate(req.body.password)
.then(results => {
// Do stuff with results here...
});

What is the good way to break from promise chain?

I am wondering how can I properly break promise chain in JS.
In this code, I am connecting to database at first, then I am checking if collection already has some data, if not add them. Don't pay attention to some actionhero.js code..it does not matter here.
The main question is: Is it okay to break chain using throw null?
mongoose.connect(api.config.mongo.connectionURL, {})
.then(() => {
return api.mongo.City.count();
})
.then(count => {
if (count !== 0) {
console.log(`Amout of cities is ${count}`);
throw null; // break from chain method. Is it okay ?
}
return api.mongo.City.addCities(api.config.mongo.dataPath + '/cities.json');
})
.then(cities => {
console.log("Cities has been added");
console.log(cities);
next();
})
.catch(err => {
next(err);
})
Many thanks!
Despite it may seem like a clever trick and will work as You expect, I would advise against throwing non-error objects.
It would be much more predictable for other developers that will maintain this code if You throw an actual Error and handle it explicitly.
Promise
.resolve()
.then(() => {
throw new Error('Already in database');
})
.catch(err => {
if (err.message === 'Already in database') {
// do nothing
} else {
next(err);
}
});

Categories