I'm not super familiar with promises, but I have a few that I'm implementing within my code and would really like an alert to appear from the client side when the promise is rejected.
Here is the socket I'm calling client side
socket.emit('updateExistingItem', NewItem, (boolean) => {});
NewItem is an object that contains the fields that I want to send.
This calls a promise in my inventory.js file
export const updateExistingItem = (ItemObject) => {
return new Promise(async (resolve, reject) => {
try {
const updateExistingItem = `UPDATE console.inventory_items SET Description = '${(ItemObject.Description).replace(/\'/g, "")}', Location = '${(ItemObject.Location).replace(/\'/g, "")}', ModelNumber = '${(ItemObject.ModelNumber).replace(/\'/g, "")}'`
const response = await db(updateExistingItem, `Updating Item`);
resolve(response);
} catch (e) {
console.log("ERROR inventory.updateExistingItem: " + e);
reject(e);
}
});
};
If a user on the client side puts in information in the NewItem object that doesn't cooperate with the SQL call, I need a way to alert that user that the information wasn't saved/updated. As of right now it's a little misleading to the user, because if they put in something that get's rejected, it looks like it succeeds from their end.
If anyone has solutions that would be fantastic! Thanks.
EDIT
Here is the socket in my sockets.js file
socket.on('updateExistingItem', async (ItemObject, callback) => {
try {
const results = await updateExistingItem(ItemObject);
if (results.affectedRows === 1) {
callback(true);
}
callback(false);
}
catch (error) {}
});
SocketIO being used in my server.js file
var server = http.createServer(app);
const io = socketIO(server);
io.on('connection', (socket) => {
require('./middleware/sockets')(socket);
});
There are 4 different files where these are used so this is why I only put the snippets that actually pertain to this specific call.
Ok so I was able to find a decent solution to this on my own.
In my socket within my sockets.js file I put a callback within the catch method like so
socket.on('updateExistingItem', async (ItemObject, callback) => {
try {
const results = await updateExistingItem(ItemObject);
if (results.affectedRows === 1) {
callback(true);
}
callback(false);
}
catch (error) {
callback(false);
}
});
From there I decided to console log the boolean value that is found in the socket.emit on the client side, and was able to get this callback value in return.
Now it looks something like this
socket.emit('updateExistingItem', NewItem, (boolean) => {
if(boolean){
alert('Successful!');
}else{
alert('UnSuccessful!');
}
});
I now get updates from the client side if this promise gets rejected or resolved.
Related
I am testing an async method that returns some data from a web request using the native https.request() method in NodeJS. I am using mocha, chai, and sinon with the relevant extensions for each.
The method I'm testing essentially wraps the boilerplate https.request() code provided in the NodeJS docs in a Promise and resolves when the response 'end' event is received or rejects if the request 'error' event is received. The bits relevant to discussion look like this:
async fetch(...) {
// setup
return new Promise((resolve, reject) => {
const req = https.request(url, opts, (res) => {
// handle response events
});
req.on('error', (e) => {
logger.error(e); // <-- this is what i want to verify
reject(e);
});
req.end();
});
}
As indicated in the comment, what I want to test is that if the request error event is emitted, the error gets logged correctly. This is what I'm currently doing to achieve this:
it('should log request error events', async () => {
const sut = new MyService();
const err = new Error('boom');
const req = new EventEmitter();
req.end = sinon.fake();
const res = new EventEmitter();
sinon.stub(logger, 'error');
sinon.stub(https, 'request').callsFake((url, opt, cb) => {
cb(res);
return req;
});
try {
const response = sut.fetch(...);
req.emit('error', err);
await response;
} catch() {}
logger.error.should.have.been.calledOnceWith(err);
});
This feels like a hack, but I can't figure out how to do this correctly using the normal patterns. The main problem is I need to emit the error event after the method is called but before the promise is fulfilled, and I can't see how to do that if I am returning the promise as you normally would with mocha.
I should have thought of this, but #Bergi's comment about using setTimeout() in the fake gave me an idea and I now have this working with the preferred syntax:
it('should log request error events', () => {
const sut = new MyService();
const err = new Error('boom');
const req = new EventEmitter();
req.end = sinon.fake();
const res = new EventEmitter();
sinon.stub(logger, 'error');
sinon.stub(https, 'request').callsFake((url, opt, cb) => {
cb(res);
setTimeout(() => { req.emit('error', err); });
return req;
});
return sut.fetch(...).should.eventually.be.rejectedWith(err)
.then(() => {
logger.error.should.have.been.calledOnceWith(err);
});
});
I don't like adding any delays in unit tests unless I'm specifically testing delayed functionality, so I used setTimeout() with 0 delay just to push the emit call to the end of the message queue. By moving it to the fake I was able to just use promise chaining to test the call to the logger method.
I am writing web app which controls hardware. I have a server communicated with the device through the serial port. Everything works except the interaction with a user. The device has registers which I repeatedly ask waiting for some values. If some values come, I emit an event to the client and confirmation box appears. The user selects resume or abort. After that client emit the response (true or false) and I would like to resolve this response in my promise function. I need to catch response from the user exactly in the function because I have a sequence of actions I need to proceed. Promise after promise. It seems that my function ends before the user answers. How to solve this problem?
this is my code on the server:
waitUserResponse(message) {
return new Promise(async (resolve) => {
const handler = function(data) {
console.log('userAnswer = ', data);
resolve(data);
return;
}
this.io.sockets.emit('alerts', message);
this.io.sockets.once('userAnswer', handler);
})
}
this is my code on the client:
componentDidMount() {
const confirmDialog = (msg) => {
return new Promise((resolve) => {
let confirmed = window.confirm(msg);
resolve(confirmed);
return;
})
}
socket.on('alerts', data => {
confirmDialog(data).then(data => {
console.log(data);
socket.emit('userAnswer', data);
});
});
}
I should use socket.id - id for my connection
io.sockets.connected[socket.id].once('userResponce', handler);
Snippets are from a node.js and mongoDB CRUD application.Github repo for full code. The code is working fine but unsure if my structure and use of promises and async await are bad practice.
handlers._newbies = {};
handlers._newbies.post = (parsedReq, res) => {
const newbie = JSON.parse(parsedReq.payload);
databaseCalls.create(newbie)
.then((result) => {
res.writeHead(200,{'Content-Type' : 'application/json'});
const resultToString = JSON.stringify(result.ops[0]);
res.write(resultToString);
res.end();
})
.catch(err => console.log(err));
};
const databaseCalls = {};
databaseCalls.create = (newbie) => {
return new Promise(async (resolve, reject) => {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
console.log("Connected correctly to server");
const db = client.db('Noob-List');
const result = await db.collection('newbies').insertOne(newbie);
client.close();
resolve(result);
} catch(err) {
console.log(err);
}
});
};
When the node server gets a POST request with the JSON payload, it calls the handlers._newbies.post handler which takes the payload and passed it to the
const newbie = JSON.parse(parsedReq.payload);
databaseCalls.create(newbie)
call. I want this database call to return a promise that holds the result of the db.collection('newbies').insertOne(newbie);
call. I was having trouble doing this with just returning the promise returned by the insertOne because after returning I cant call client.close();.
Again maybe what I have done here is fine but I haven't found anything online about creating promises with promises in them. Thank you for your time let me know what is unclear with my question.
It is considered an anti-pattern to be wrapping an existing promise in a manually created promise because there's just no reason to do so and it creates many an opportunities for error, particular in error handling.
And, in your case, you have several error handling issues.
If you get an error anywhere in your database code, you never resolve or reject the promise you are creating. This is a classic problem with the anti-pattern.
If you get an error after opening the DB, you don't close the DB
You don't communicate back an error to the caller.
Here's how you can do your .create() function without the anti-pattern and without the above problems:
databaseCalls.create = async function(newbie) {
let client;
try {
client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
console.log("Connected correctly to server");
const db = client.db('Noob-List');
return db.collection('newbies').insertOne(newbie);
} catch(err) {
// log error, but still reject the promise
console.log(err);
throw err;
} finally {
// clean up any open database
if (client) {
client.close();
}
}
}
Then, you would use this like:
databaseCalls.create(something).then(result => {
console.log("succeeded");'
}).catch(err => {
console.log(err);
});
FYI, I also modified some other things:
The database connection is closed, even in error conditions
The function returns a promise which is resolved with the result of .insertOne() (if there is a meaningful result there)
If there's an error, the returned promise is rejected with that error
Not particularly relevant to your issue with promises, but you will generally not want to open and close the DB connection on every operation. You can either use one lasting connection or create a pool of connections where you can fetch one from the pool and then put it back in the pool when done (most DBs have that type of feature for server-side work).
I am having challenges retrieving the results of my mutation. I need to create a db record and send an email notifying to user that the registration was successful. since both the sending of the email and the db update is server side I want to do both in the same mutation. If the email message fail the db must not be updated. So I have the following Mutation:
Mutation: {
createDealer(_, params) {
console.log("params: " + JSON.stringify(params));
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(params.dealer.password, salt, function(err, hash) {
// Store hash in your password DB.
console.log("hashed password " + params.dealer.password)
params.dealer.password = hash;
console.log("hashed password " + params.dealer.password + " Hash: " + hash);
let session = driver.session();
let query = "CREATE (d:Dealer {email:$dealer.email}) SET d += $dealer RETURN d";
let here = "here".link("mymail#example.com");
let messageObj = {
to: params.dealer.email,
subject: 'Dealer Registration',
text: `Thank you for signing up. To complete and activate your registration please click ${here}.`
}
return (sendEmail(messageObj))
.then(data => {
console.log('SendMail data....' + JSON.stringify(data));
return session.run(query, params)
})
.then(result => {
console.log('SendNeo4j data....' + JSON.stringify(result));
return result.records[0].get("d").properties
})
.catch((err) => {
console.log(err);
});
//});
});
}); // genSalt
} // Create Dealer
}, // Mutation
Even thought both actions are successful I can't seem to retrieve the results. I get 'undefined' for:
console.log('SendMail data....' + JSON.stringify(data));
while
console.log('SendNeo4j data....' + JSON.stringify(result));
does display the correct data
but graphiql returns 'null' for the mutate.
this is the graphiql mutation:
mutation CreateDealer($dealer: DealerInput!) {
createDealer(dealer: $dealer) {
email
name
}
}
with the DealerInput variables of course.
I have read where you can retrieve multiple results from a query/mutation but I am not sure how it works. Here I need both the results of the sendEmail and the db update for my Angular/apollo front-end....I would imaging graphiql knows nothing of the sendEmail but I expected it to return the properties I requested.
SendEmail:
module.exports = (message) =>
new Promise((resolve, reject) => {
const data = {
from: 'mymail#example.com',
to: message.to,
subject: message.subject,
text: message.text
};
mailgun.messages().send(data, (error) => {
if (error) {
return reject(error);
}
return resolve();
});
});
Can someone with a little more experience than I help me out here...thanks
Couple of things to fix here. Returning a Promise (or any other value) inside a callback doesn't do anything, and doing so won't let you chain additional Promises like you want. Instead, your promise gets fired off inside the callback and isn't awaited.
As a general rule of thumb, don't mix Promises and callbacks. If you absolutely have to use callbacks, always wrap the callback in a Promise (like you did inside sendMail). Luckily, most popular libraries today support both callbacks and Promises. Here's how you could refactor the code above to correctly chain all your Promises:
createDealer(_, params) {
return bcrypt.hash(params.dealer.password, 10) // note the return here!
.then(hash => {
params.dealer.password = hash
const session = driver.session()
const query = "CREATE (d:Dealer {email:$dealer.email}) SET d += $dealer RETURN d"
const here = "here".link("mymail#example.com")
const messageObj = {
to: params.dealer.email,
subject: 'Dealer Registration',
text: `Thank you for signing up. To complete and activate your registration please click ${here}.`
}
return sendEmail(messageObj) // note the return here!
}).then(data => {
return session.run(query, params) // note the return here!
}).then(result => {
result.records[0].get("d").properties // note the return here!
})
bcrypt.hash will autogenerate the salt for you if you don't pass one in -- there's no need to call two separate functions
We kick off our Promise chain with bcrypt.hash, so we need to return the Promise it returns. A resolver must return a value or a Promise that will resolve to a value, otherwise it returns null.
Inside each then, we return a Promise. This way we "chain" our Promises, allowing the final value we return in the resolver to be the value the very last Promise in the chain resolves to.
We need to also fix your sendMail function to actually return the value. You're correctly returning the new Promise inside the function, but you also need to pass the returned data object to resolve. That tells the Promise to resolve to that value.
module.exports = (message) => new Promise((resolve, reject) => {
const data = // ...etc
mailgun.messages().send(data, (error) => {
if (error) reject(error) // no need to return, it's pointless
resolve(data) // pass data to resolve
})
})
Side note: looks like the official mailgun library supports Promises.
Additionally, I would strongly encourage you to look into using async/await, especially when dealing with a long Promise chain. It's less error prone and more readable:
createDealer async (_, params) {
const hash = await bcrypt.hash(params.dealer.password)
params.dealer.password = hash
const session = driver.session()
const query = "CREATE (d:Dealer {email:$dealer.email}) SET d += $dealer RETURN d"
const here = "here".link("mymail#example.com")
const messageObj = {
to: params.dealer.email,
subject: 'Dealer Registration',
text: `Thank you for signing up. To complete and activate your registration please click ${here}.`
}
const emailResult = await sendEmail(messageObj)
const result = await session.run(query, params)
return result.records[0].get("d").properties // still need to return!
}
EDIT: With regard to catching errors, GraphQL will catch any errors thrown by your resolver, which means you can often skip using catch yourself. For example, if your mailgun request fails, it'll generate some kind of error and your query will return null for data and the error details inside of the errors array.
That may be sufficient, although 1) you may want to log your error's stack elsewhere; and 2) in production, you probably don't want to expose internal error details to the public.
That means you'll probably want to use custom errors. As a bonus, you can add some custom properties to your errors to help the client deal with them eloquently. So your code may end up looking more like this:
class DeliveryFailureError extends Error {}
DeliveryFailureError.code = 'DELIVERY_FAILURE'
DeliveryFailureError.message = 'Sorry, we could not deliver the email to your account'
try {
await mailgun.messages.create()
} catch (err) {
logger.error('Mailgun request failed:', err.stack)
throw new DeliveryFailureError()
}
I'm creating an app that will use the https://github.com/vpulim/node-soap to communicate with a soap server.
I would like to create a client component which I will forward the necessary methods to the soap-client created with this module.
I'm having trouble to return an object that will use this client, since the client is created asynchronously.
var soap = require('soap');
var url = 'http://someurl?wsdl';
soap.createClient(url, function(err, client) {
if (err) {
console.log(err);
return;
}
console.log(client.describe());
// I need to publish this client so that other functions in this file will be able to use it
});
module.exports = {
doSomething: function() {
//how can I access the client here?
}
};
How would I go about doing this?
One solution to this problem is to use promises:
var soap = require('soap');
var url = 'http://someurl?wsdl';
var clientPromise = new Promise(function(resolve, reject) {
soap.createClient(url, function(err, client) {
if (err) {
// reject the promise when an error occurs
reject(err);
return;
}
// resolve the promise with the client when it's ready
resolve(client);
});
});
module.exports = {
doSomething: function() {
// promise will wait asynchronously until client is ready
// then call the .then() callback with the resolved value (client)
return clientPromise.then(function(client) {
// do something with client here
}).catch(function(err) {
// handle errors here
console.error(err);
});
}
};
A few advantages to this:
Promises are native JavaScript objects (as of Node 4.0.0, with packages such as bluebird providing support for prior versions)
Promises can be "reused": if clientPromise has already resolved once, it will immediately resolve when doSomething is later called.
Some disadvantages:
doSomething and all other exported functions are inherently asynchronous.
Is not directly compatible with Node-type callbacks.
Not sure if my response helps you, but this is how I do. I create createClient every time and then within the client, I call the actual SOAP method (here GetAccumulators). May be not a great way, but this works for me. Here is my code sample
soap.createClient(url, function (err, client) {
if (err) {
logger.error(err, 'Error creating SOAP client for %s', tranId);
reject('Could not create SOAP client');
}
client.addSoapHeader(headers);
// envelope stuff
client.wsdl.definitions.xmlns.soapenv = 'http://schemas.xmlsoap.org/soap/envelope/';
client.wsdl.definitions.xmlns.acc = 'http://exampleurl/';
client.wsdl.xmlnsInEnvelope = client.wsdl._xmlnsMap();
client.GetAccumulators(args, function (err, result) {
if (err) {
if (isNotFoundError(err)) {
logger.debug('Member not found for tranId %s', tranId);
reject({status:404, description:'No match found'});
}
reject({status:500, description:'GetAccumulators error'});
}
return resolve({data: result, tranId: tranId});
});