Javascript Promise resolving in catch block - javascript

I have a function sendMail that returns a promise.
module.exports = function sendMail(mailData) {
const smtpConfig = {
service: 'gmail',
auth: {
user: MAIL_USER,
pass: MAIL_PASS
}
};
const trans = nodemailer.createTransport(smtpConfig);
return new Promise((reject, resolve) => {
trans.sendMail(mailData, (err, result) => {
if (err) {
console.log('error', err)
reject(err);
}
else {
console.log('no error', result)
resolve(result);
}
});
});
}
The promise resolves and the logged output from this is:
no error { accepted: [ 'test#test.com' ],
rejected: [],
response: '250 2.0.0 OK 1488900621 j79sm6419250itb.0 - gsmtp',
envelope: { from: '', to: [ 'test#test.com' ] },
messageId: '<511ca80b-0bec-2d06-8f52-81194bcbf26b#My-MacBook-Pro.local>' }
And the email is sent.
However calling the function from another module, the result gets passed to the .catch block as an error.
module.exports = User = {
sendMail(mailData)
.then((result) => {
console.log('here', result)
})
.catch((err) => {
console.log('error', err)
});
}
Produces the identical output but as an error in the catch block:
error { accepted: [ 'test#test.com' ],
rejected: [],
response: '250 2.0.0 OK 1488900621 j79sm6419250itb.0 - gsmtp',
envelope: { from: '', to: [ 'test#test.com' ] },
messageId: '<511ca80b-0bec-2d06-8f52-81194bcbf26b#MY-MacBook-Pro.local>' }
And the .then block never runs.
Any help as to what I'm missing here is greatly appreciated.

Here:
return new Promise((reject, resolve) => {
you mixed up the order of function parameters. Your function will be passed resolve first and reject second no matter how you name it, so your resolve will act as reject and vice versa.
It should be:
return new Promise((resolve, reject) => {
The names of the arguments to the function that you pass to the Promise constructor doesn't matter, but the order matters. So for example you can use this to produce a resolved promise:
new Promise((x, y) => x(123));
// same as Promise.resolve(123);
or this to produce a rejected promise:
new Promise((x, y) => y(123));
// same as Promise.reject(123);
but if you switch the x and y then it will be different - now x rejects:
new Promise((y, x) => x(123));
// same as Promise.reject(123);
and y resolves:
new Promise((y, x) => y(123));
// same as Promise.resolve(123);
See the docs:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
Syntax
new Promise( /* executor */ function(resolve, reject) { ... } );
So the mystery is not why it doesn't work but why it works in some other module. Maybe it does something like:
Promise.reject(3).catch(e => e).then(val => console.log('val:', val));
but using your function instead of Promise.reject(3) and using some other logic instead of console.log, of course.

Related

Jest returning false positives for Firebase functions?

Having to write some Jest tests for Firebase functions and I'm running into a little trouble. Here's a basic example of what I'm trying to do:
databaseUtils.js
function funcOne() {
return Promise.resolve();
}
function funcTwo() {
return Promise.resolve();
}
index.js
exports.myTest = functions.https.onRequest(async (req, res) => {
try {
await databaseUtils.funcOne();
await databaseUtils.funcTwo();
return res.status(200);
} catch (error) {
console.log(error);
return res.status(403);
}
});
index.test.js
describe('myTest', () => {
it('test 1', async () => {
const req = {}
const res = {
status: jest.fn()
};
await functions.myTest(req, res);
expect(res.status).toHaveBeenCalledWith(200)
});
it('test 2', async () => {
const req = {}
const res = {
status: code => {
expect(code).toBe(403);
}
};
await functions.myTest(req, res);
});
it('test 3', (done) => {
const req = {}
const res = {
status: code => {
expect(code).toBe(403);
done();
}
};
functions.myTest(req, res);
});
})
When I run my tests, Test 1 fails while Test 2 and 3 pass, but with the wrong values:
JestAssertionError: expect(received).toBe(expected) // Object.is equality
Expected: 403
Received: 200
matcherResult: {
actual: 200,
expected: 403,
message: [Function],
name: 'toBe',
pass: false
}
Been running around in circles trying to get this to work properly but I can't figure out, what am I doing wrong?
Once I've got this figured out I'd then mock funcOne / funcTwo to return a rejected promise so I can get the right status code.
onRequest is promise-aware and expected to return a promise of the entire work it does. That there's a dangling promise inside onRequest callback is an antipattern.
It should be:
return auth.decodeToken(req)...
That there are raw promises with then and catch inside async contributes to the problem because async..await is sugar syntax that allows to avoid several common problems with promises. The said problem as well as nested promises could be avoided if this were written as:
try {
const decodedToken = await auth.decodeToken(req);
...
} catch (err) {
...
}

Reduce on list of promises

I have a list of jobs that should be sequentially executed.
As the jobs take seconds to be finished the should run in the background.
I thought that a job could be described as
interface Job {
name: string
execute(): Promise<boolean>
}
I would like to have a function which takes this list of jobs and execute them sequentially
until the list is completed or one job fails or is rejected, so basically:
function executeUntilFailed(jobs: Job[]): Promise<boolean>
{
// execute first job
// if this job
// - returns with true: continue with the next job
// - returns with false: resolve the promise with false
// - got rejected: reject the promise with the reason prefixed with the jobs name
//
// if there are no more jobs to do, resolve the promise with true
//
// basically it's a reduce operation with starting value of true and
// early stops if one job yields false or got rejected
}
I'm rather new to Javascript/Typescript and have a hard time implementing this.
Thanks,
Dieter
Thanks to Aluan Hadded and ehab.
I collected their solutions and have now the following code,
which does exactly what I need:
interface Job {
name: string
execute(): Promise<boolean>
}
async function executeUntilFailed(jobs: Job[]) {
for (const job of jobs) {
try {
if(!await job.execute()) {
return false
}
}
catch (err) {
throw new Error(`${job.name}: ${err.message}`)
}
}
return true
}
and here is some example for it
class JobImpl implements Job {
constructor(public name: string, private value: boolean, private throwMsg: string|null = null) {}
execute(): Promise<boolean> {
console.log(`executing job '${this.name}'`)
return new Promise((resolve,reject) => {
setTimeout(()=> {
if(this.throwMsg!=null) { reject(this.throwMsg) }
else { console.log(`finished job '${this.name}' with result: ${this.value}`); resolve(this.value) }
}, 1000)
})
}
}
const successJobs = [
new JobImpl("a", true),
new JobImpl("b", true),
new JobImpl("c", true),
]
const failedJobs = [
new JobImpl("d", true),
new JobImpl("e", false),
new JobImpl("f", true),
]
const throwingJobs = [
new JobImpl("g", true),
new JobImpl("g", true, "undefined problem"),
new JobImpl("i", true),
]
executeUntilFailed(successJobs)
.then((res) => console.log("resolved", res))
.catch((err) => console.log("rejected", err))
executeUntilFailed(failedJobs)
.then((res) => console.log("resolved", res))
.catch((err) => console.log("rejected", err))
executeUntilFailed(throwingJobs)
.then((res) => console.log("resolved", res))
.catch((err) => console.log("rejected", err))
<!-- end snippet -->
Just as an alternative, you could create a generator and then use the for await ... of syntax:
function * chainJobs(jobs) {
for (const job of jobs) {
yield job.execute().catch(err => new Error(`${job.name}: ${err.message}`));
}
}
async function executeUntilFailed(jobs) {
for await (const result of chainJobs(jobs)) {
if (!result) return false;
if (result instanceof Error) throw result;
}
return true;
}
You could achieve this either with a reduce function or a for of loop, i will show an implementation in a for of
async function executeUntilFailed(jobs) {
for (const job of jobs) {
try {
// notice that if a job resolved with false then it is considered a successful job
// This is normal and a promise resolved with false should not be considered an error
await job.execute()
// if u want based on your description to resolve the whole promise with false if one of promises resolved with false you could do
// const jobResult = await job.execute()
// if (jobResult === false) {
// return Prmise.resolve(false)
// }
} catch (err) {
return Promise.reject(new Error(`${job.name}_${err.toString()}`))
}
}
return Promise.resolve(true)
}
lets see the function in action
const successJobs = [{
name: "a",
execute: () => Promise.resolve(1)
},
{
name: "b",
execute: () => Promise.resolve(2),
},
{
name: "c",
execute: () => Promise.resolve(3)
},
]
const failedJobs = [{
name: "a",
execute: () => Promise.resolve(1)
},
{
name: "b",
execute: () => Promise.reject(new Error("undefined problem")),
},
{
name: "c",
execute: () => Promise.resolve(3)
},
]
async function executeUntilFailed(jobs) {
for (const job of jobs) {
try {
await job.execute()
} catch (err) {
return Promise.reject(new Error(`${job.name}_${err.toString()}`))
}
}
return Promise.resolve(true)
}
console.log(
executeUntilFailed(successJobs)
.then((res) => console.log("resolved", res))
.catch((err) => console.log("rejected", err))
)
console.log(
executeUntilFailed(failedJobs)
.then((res) => console.log("resolved", res))
.catch((err) => console.log("rejected", err))
)

Promise.all response

Can I add Promise.resolve(value) and Promise.reject(error) at response of Promise.all().
For example,
function transferFRQ(fromUserId, fromCompanyDetails, toUserDetails, toCompanyDetails,) {
return Promise.all([
transferRFQCompany(fromCompanyDetails, toCompanyDetails),
replaceRFQCreatedBy(fromUserId, toUserDetails)
])
.then(result => Promise.resolve(result))
.catch(error => Promise.reject(error));
}
function transferRFQCompany (fromCompanyDetails, toCompanyDetails) {
return new Promise((resolve, reject) => {
Request.updateMany({
"company.id": fromCompanyDetails._id
}, {
$set: {
company: {
id: toCompanyDetails._id,
name: toCompanyDetails.name,
logo: toCompanyDetails.logo
}
}
}).then(result => resolve(result))
.catch(error => reject(error));
});
}
function replaceRFQCreatedBy (fromUserId, toUserDetails) {
return new Promise((resolve, reject) => {
Request.updateMany({
"createdBy.id": fromUserId
}, {
$set: {
createdBy: {
id: toUserDetails._id,
firstName: toUserDetails.firstMame,
lastName: toUserDetails.lastName
}
}
}).then(result => resolve(result))
.catch(error => reject(error));
});
}
I don't know whether is it correct or not, but what I need is to handle the response of transferRFQ properly because I will need to add transferRFQ in another Promise.all() to handle the error properly.
Am I doing in the wrong way? if so, How to do it correctly
Any additional advice is welcome!
I advice that you should not use unnecessary Promise wrappers, & avoid javascript hoisting.
You should try to do something like this instead
// Note that this is declared before used, to avoid javascript hoisting
function transferRFQCompany (fromCompanyDetails, toCompanyDetails) {
return Request.updateMany({ // updateMany already returns a promise right? no need to wrap it in another promise
"company.id": fromCompanyDetails._id
}, {
$set: {
company: {
id: toCompanyDetails._id,
name: toCompanyDetails.name,
logo: toCompanyDetails.logo
}
}
})
});
}
// Note that this is declared before used, to avoid javascript hoisting
function replaceRFQCreatedBy (fromUserId, toUserDetails) {
return Request.updateMany({ // updateMany already returns a promise right? no need to wrap it in another promise
"createdBy.id": fromUserId
}, {
$set: {
createdBy: {
id: toUserDetails._id,
firstName: toUserDetails.firstMame,
lastName: toUserDetails.lastName
}
}
})
}
function transferFRQ(fromUserId, fromCompanyDetails, toUserDetails, toCompanyDetails,) {
return Promise.all([
transferRFQCompany(fromCompanyDetails, toCompanyDetails),
replaceRFQCreatedBy(fromUserId, toUserDetails)
])
}
// Sample usage async/await style
(async () => {
try {
// put your params, of course
const result = await transferFRQ(...params);
// `result` is result of .then()
} catch (e) {
// `e` is result of .catch()
}
// or use it in promise-style
transferFRQ(...params)
.then(console.log)
.catch(console.error)
})()

Handle error in promise ES6 nodejs

Hi I am new to ES6 and I am using promise chain
I am not getting error catch in my promise chain.
let cost, stars;
getStarInfo(req.body.star_id).then( (star) => {
let stripe_object = new Stripe(req.body.stripe_token, cost);
return stripe_object.makepayment();
}).then( (payment) => {
console.log(1 ,payment);
return savePurchase(req.decoded._id, cost, stars, payment.id);
}).catch( (err) => {
res.json({'success' : false , 'err' : err , msg : 'Something went wrong please try again'});
});
My savePurchase function is like this
function savePurchase( cost , stars, payment_id){
console.log("hello")
return new Promise( (resolve, reject) => {
var purchasedstars = new Stars({
user_id : user_id,
stars : stars,
money_earned : cost,
transaction_id : payment_id
});
console.log(purchasedstars)
purchasedstars.save(function(err , saved_doc){
console.log('save' , err , saved_doc)
if(err){
reject(err)
}else{
resolve(saved_doc);
}
});
});
}
In savePurchase function if my user_id is undefined, the promise does not give me error. It just goes in the catch and give empty error object. How can I find out the error in my function.?
After returning a new promise from savePurchase you chaining your catch with it, but not with getStarInfo promise anymore, so you have no error handler for getStarInfo promise.
.then() takes an optional second function to handle errors
var p1 = new Promise( (resolve, reject) => {
resolve('Success!');
// or
// reject ("Error!");
} );
p1.then( value => {
console.log(value); // Success!
}, reason => {
console.log(reason); // Error!
} );
Define custom error and reject.
purchasedstars.save(function (err, saved_doc) {
console.log('save', err, saved_doc)
if (err) {
reject({
err,
message: 'Some error message'
})
} else {
resolve(saved_doc);
}
});

JavaScript testing promise

I'm trying to test a function which recives a promise like this
{
state: 'fulfilled'
value:[Array]
}
function register (foo) {
return new Promise((resolve,reject)=>{
query = "INSERT IGNORE INTO....";
connection.query(query, [foo.value.from, foo.value.id], (err, res, fields) => {
if(err){
return undefined //this will change
}
return foo
})
})
}
The thing is I am returning, non rejecting neither resolving. So when Im testing..
it('insertion error', function () {
var res = 'error'
connection = {
query: (query, input, cb) =>{
return cb(res, null, null)
}
}
let database = proxyquire('path/to',{
'./connection': connection
})
var input =
{
value: {
id: 'bar',
data: [],
from: 'foo'
}}
return database.register(input)
.then( (result) => {
expect(result).to.be.undefined
})
.catch( (err) => {
console.log(err)
err = new Error (`Test fail: ${err}`)
throw err;
})
})
The function works well, Im pretty sure of that.
The other thing I'm sure of is that the THEN/CATCH condition never shows up. I know if I replace the return for resolve it will show up, but I need to be returned.
What should I modify, or how should I test it.
This is a basic example how can you make a promisse, in this example you can send true or false in the register() function.
function register ( foo ) {
return new Promise( (resolve, reject) =>{
if(foo){
resolve('Done');
}else{
reject('Error')
}
} )
}
register(false).then( result=>{
document.write('promisse success... ', result);
} ).catch( error => {
document.write('promisse error... ', error);
} )

Categories