Async Method Chaining? [duplicate] - javascript

This question already has answers here:
JS - chain async methods in sequence w/o callback or modification
(3 answers)
Closed 4 years ago.
I'm trying to create a class with async method chaining. However, I'm still setting the new values before I fully fetch the data from the database. Am I missing something?
I also don't want to use any third-party modules.
/* Class */
class UserDB {
constructor() {
this.user = {}
this.user.username = null
}
set(name, value) {
this.user[name] = value
return this
}
update() {
return new Promise((resolve, reject) => {
const db = MongoConnection.client.db('database').collection('users')
db.updateOne({ _id: this.user._id }, { $set: this.user}, (err, result) => {
if (err) reject(err)
resolve(this)
})
})
}
findByEmail(email) {
return new Promise((resolve, reject) => {
const db = MongoConnection.client.db('database').collection('users')
db.findOne({ email: email }, (err, result) => {
if (err) reject(err)
this.user = result
resolve(this)
})
})
}
}
module.exports = UserDB
/*execution*/
new UserDB().findByEmail('email#email.com')
.set('username', 'new_name')
.update()

Thats indeed interesting. You could overload the returned Promise with methods that attach the operation to a .then chain:
class AsyncChainable {
constructor() {
this._methods = {};
const that = this;
for(const key of Object.getOwnPropertyNames(Object.getPrototypeOf(this))) {
this._methods[key] = function(...args) {
return that.chain(this.then(() => that[key](...args)));
}
}
chain(promise) { return Object.assign(promise, this._methods); }
}
Then use that in your custon class:
class User extends AsyncChainable {
login() {
return this.chain((async () => {
// some logic
})());
}
}
That way you can do:
(new User).login().login().then(console.log);

Related

Return a value in a inner function on strong-soap

I am working with soap in Node typescript.
I want to return a boolean after performing a soap request to the server in the strong-soap package.
The code looks like:
public isSupplierBalance(debtorAccount: number): boolean {
let economicToken = this.token;
soap.createClient(this.WSDL, {}, function (err, client) {
let method = client['ConnectWithToken'];
let args = {
token: economicToken,
appToken: economicAPI.EconomicN1Key
};
method(args, function (err, result, envelope, soapHeader) {
if (err) {
console.log(err);
}
let session = cookieParser(client.lastResponseHeaders);
let args = {
creditorHandle: {
Number: debtorAccount
}
};
client.addHttpHeader('Cookie', session);
method = client['Creditor_GetOpenEntries'];
method(args, function (err, result, envelope, soapHeader) {
if (err) {
console.log(err);
}
console.log(result.toString());
return (typeof result.Creditor_GetOpenEntriesResult === 'undefined');
});
});
});
}
Basically I want to return if the result.Creditor_GetOpenEntriesResult contains any values of not. I have seen in the documentation of strong-soap that an async approach can be used, and I guess that should be my approach but any test towards that solution have just failed.
https://www.npmjs.com/package/strong-soap
Hope this helps. I was able to wrap it with a new Promise
async function callSOAP(options: {}, methodname: any, methodparams: any) {
let finalresult: any;
return new Promise((resolve, reject) => {
soap.createClient(url, options, async function (err: any, client: any) {
const { result, envelope, soapHeader } = await client[methodname](methodparams);
finalresult = { result, envelope, soapHeader }
// return { result, envelope, soapHeader };
if (err) {
reject(err);
}
else { resolve(finalresult) };
});
}
The calling code would look like this
result = await callSOAP(options, methodname, methodparams).then((r1) => r1);

"this" key word is undefined while calling static method inside another static method (JavaScript)

I'm exporting/importing an empty array to implement MVC patern and I was able to successfully store data in it by calling the allMentors method on the Mentor class.
Secondly, I want to be DRY and get the data from the array and then use it to find a specific mentor but I'm getting an empty array.
I searched the internet and I followed the example of using this keyword and call the static method inside another but NodeJS is throwing an error that this is undefined.
ALL MENTORS method
class Mentor{
static async allMentors(req, res) {
try {
users.forEach(user => {
if(user.is_mentor === true) {
mentors.push(user);
}
})
const ObjKeyRename = (src, map) => {
const dst = {};
for (const key in src) {
if (key in map)
// rename key
dst[map[key]] = src[key];
else
// same key
dst[key] = src[key];
}
return dst;
};
const uniqueMentors = Array.from(new Set(mentors.map(m => m.id)))
.map(id => {
return new Promise((resolve, reject)=> {
const currMentor = mentors.find(m => m.id === id);
const modMentor = ObjKeyRename(currMentor, { "id": "mentorId" });
return resolve(modMentor);
})
})
Promise.all(uniqueMentors).then(output => {
output.forEach(async obj => {
await delete obj['password'];
})
return res
.status(200)
.json(new ResponseHandler(200, 'All Mentors', output, null).result());
})
} catch (err) {
return res
.status(500)
.json(new ResponseHandler(500, err.message, null).result());
}
}
static async singleMentor(req, res) {
const returnedMentors = this.allMentors;
console.log('THE RETURNED:',returnedMentors)
const theMentor = returnedMentors.find(u => u.mentorId === parseInt(req.params.mentorId));
try {
if (!theMentor) return res
.status(404)
.json(new ResponseHandler(404, `Mentor number ${req.params.mentorId} not found`, null).result());
return res
.status(200)
.json(new ResponseHandler(200, 'Your mentor', theMentor, null).result());
} catch (err) {
return res
.status(500)
.json(new ResponseHandler(500, err.message, null).result())
}
}
}
export default Mentor;
What am I doing wrong? I appreciate your continued help as a JS learner.
There are multiple problems with your code:
a static method needs to be called by Class (Mentor) not by Reference (this)
a async method needs to be awaited or called with a callback (.then())
So for example you have the class MentorHandler:
class Mentor {
constructor() {
}
static async singleMentor(req, res) {
// ... some more code
}
static async allMentors(req, res) {
// await the result of the method
// call by class
const returnedMentors = await Mentor.allMentors();
// ... some more code
}
}

How to stub a class which returns a class?

I have a method which i'm unit testing and i'm trying to test both
...
let db = new MockDb()
...
db.query(query)
.then(cursor => {
return cursor.next()
.then(result => {
if (result !== undefined || !data.id) { <-- WANT TO STUB CURSOR.NEXT() SO I CAN TEST RESULT BEING DIFFERENT VALUES
data.id = uuidv4()
}
...
I have this file that is used for mocking/stubbing which contains 2 classes, but only exposes one of them
class MockDb {
query (aql) {
return new Promise((resolve, reject) => {
return resolve(new MockCursor())
})
}
}
class MockCursor {
next () {
return new Promise((resolve, reject) => {
return resolve({foo: 'bar'})
})
}
}
module.exports = MockDb
How can I stub what cursor.next() returns in this case?
Does NOT work - gives error TypeError: cursor.stub is not a function
before(() => {
let cursor = sinon.stub()
cursor.stub('next').return(undefined)
sinon.stub(db, 'query').resolves(cursor)
})
If i expose the MockCursor class I can simply do this:
let db = new MockDb()
let cursor = new MockCursor()
sinon.stub(cursor, 'next').resolves(undefined)
sinon.stub(db, 'query').resolves(cursor)
But i was just trying to do it without having to expose the MockCursor class explicitly
This should work:
const cursor = sinon
.stub()
.returns({
next: () => Promise.resolve(undefined)
});
sinon.stub(db, 'query').resolves(cursor);

Function that calls function containing async query to mongodb.find [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 4 years ago.
Is there a way to call function that contains mongoose query inside other function, so other function will work properly?
My first function containing mongoose query:
getUserTags = (id) => {
User.findById(id)
.exec( (error, user) => {
if (error) {
return next(error);
} else {
return user;
}
})
}
and my functions that needs to call that first function:
userTagToBookTagValues = (id) => {
const user = getUserTags(id);
//I NEED THIS PART TO WORK AFTER getting data from getUserTags
console.log(user);
user.tags.forEach(tag => {
console.log(tag)
});
}
Is there a way so it works properly and user in second function will not be undefined?
You maybe need to return User.findById?
Like so:
getUserTags = (id) => {
return User.findById(id)
.exec( (error, user) => {
if (error) {
return next(error);
} else {
return user;
}
})
}
As I understand .exec() do not return anything, so using .then gives error.
However if rewritten like this, it works perfectly:
getUserTags = (id) => {
return User.findById(id)
}
userTagToBookTagValues = (id) => {
const user = getUserTags(id).then((user) => {
user.tags.forEach(tag => {
console.log(tag)
});
}) ;
}
findById is an asynchronous method.
If your node version greater then 8
getUserTags = id => User.findById(id).exec();
userTagToBookTagValues = async (id) => {
try {
const user = await getUserTags(id);
user.tags.forEach(tag => {
console.log(tag)
});
} catch (err) {
next(err);
}
}
Or you can use then method after calling getUserTags function.
For more information about of asnc methods:
Medium

how to return new record from graphql mutation instead of null

I read through the apollo-server-tutorial and I'm trying to replicate it with only mongodb - no sqllite. I'm coming from meteor so I'm learning async & mongoose while I'm at it.
My mutation for addAuthor() works correctly (I see a new record in the DB) but graphiql returns null. How do I get it to return the expected fields?
mutation {
addAuthor(firstName: "falieson", lastName:"p") {
_id
firstName
lastName
}
}
{
"data": {
"addAuthor": null
}
}
Schema: https://github.com/Falieson/apollo-server-tutorial-only-mongodb/blob/master/data/schema.js#L39
Resolver: https://github.com/Falieson/apollo-server-tutorial-only-mongodb/blob/master/data/resolvers.js#L38
Mongoose: https://github.com/Falieson/apollo-server-tutorial-only-mongodb/blob/master/data/models/mongoose.js#L51
Model: https://github.com/Falieson/apollo-server-tutorial-only-mongodb/blob/master/data/models/index.js#L7
I needed to have mongoose return a promise for the mutation. So my MongooseModel.create() changes from
const record = new this.collection(args)
return record.save(cb)
to using Promise
const record = new this.collection(args)
return new Promise((resolve, reject) => {
record.save((err, res) => {
err ? reject(err): resolve(res)
});
});
to using Async/Await
async create(obj = {}) {
const args = {
...this.defaultRecord(),
...obj
}
const record = new this.Collection(args)
try {
const savedRecord = await record.save()
return savedRecord
} catch (err) {
handleError(err)
}
}
My mutation doesn't have to change at all, but my fixtures generator also has to be updated to use the Promise.then() chain.

Categories