0

I'm building classes to find and quickly operate actions on mongodb documents. This is the UserCursor class. (Not talking about MongoDB's cursor)

exports { UserCursor };
class UserCursor {

    private __id: object;

    constructor(query: { _id?: object, otherId?: number }) {
        let { _id, otherId } = query; // Shortens the vars' name
        if (!_id && !otherId) return; // Checks if 1 identifier is provided

        if (_id) { // If a _id is provided
            Users.findOne({ _id }, (err, user) => {
                this.__id = user._id;
            });
        } else if (otherId) { // If a otherId is provided
            Users.findOne({ otherId }, (err, user) => {
                console.log(1); // Debug, you'll see later
                this.__id = user._id;
            });
        }
    }


    // Returns this.__id (which should have been initialized in the constructor)
    get _id() {
        console.log(2)
        return this.__id;
    }

}

When run, the console returns

2
1

I think you got the problem: the mongo callback in the constructor gets on after _id operates. How could I manage that, since the constructor gets activated each time the class is used?

deb
  • 6,671
  • 2
  • 11
  • 27
  • You have to use a factory if you need asynchronous initialization. Also, all of those methods you're passing call backs to return promises so you should use that API instead – Aluan Haddad Aug 15 '20 at 15:57

1 Answers1

1

It's not entirely clear to me, what exactly you want to happen and how you use this class but I assume you want to instantiate it and then be able to get _id instantaneously. If it's not the case, you may still get some useful info from my answer. Feel free to provide more details, I will update it.

So mongodb operations are asynchronous, if you do

const cursor = new UserCursor(...)
console.log(cursor._id)

(I assume you want this), first all operations in this thread will run, including the call to get _id(), then the callback code will. The problem with such asynchronous things is that now to use this _id you will have to make all of your code asynchronous as well.

So you will need to store a Promise that resolves with _id from mongodb and make a method getId that returns this promise like this:

private __id: Promise<object>

constructor(...) {
  // ...
  if(_id) {
    this.__id = new Promise((resolve, reject) => {
       Users.findOne({ _id }, (err, user) => {
          if(err) return reject(err)
          resolve(user._id)
       });
    })
  } else {
    // Same here
  }
}

public getId() {
   return this.__id;
}

Then use it:

const cursor = new UserCursor(...)
cursor.getId().then(_id => {
  // Do smth with _id
})

Or

async function doStuff() {
  const cursor = new UserCursor()
  const _id = await cursor.getId()
}
doStuff()

If you now do it inside some function, you'll also have to make that function async

Also you could leave a getter like you have now, that will return a promise, but I find it less readable than getId():

cursor._id.then(_id => { ... })
const _id = await cursor._id
Alex Chashin
  • 3,129
  • 1
  • 13
  • 35