Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I have a constructor which fetches data from a DynamoDB using promisified dynogels to populate part of the object's properties. So after instantiating an instance of that object, the property is not populated, here is an extract of the code:

export class QueryAuthoriser {
  authPerms: [AuthPerms];

  constructor (data: string) {
    AuthPermsDDB.scan().execAsync().then ( (perms) => {
      perms.Items.forEach(element => {
        this.authPerms[element.name] = <AuthPerms> element.attrs
      })
    }).catch (err => {
      console.log ('%%%%%%%%%%%%%% Err loading authPerms: ', err)
    })
  }

  authFieldAccess (fieldName: string, args?:any): Promise<boolean> {
    return new Promise ((resolve, reject) => {
      console.log ('________________ authFieldAccess called for: ', fieldName)
      console.log ('________________ this.authPerms entry: ', this.authPerms[fieldName])
      resolve (true)
    })
[...]
}

So when authFieldAccess method is called, the field this.authPerms is undefined. How can I fix this?

Thanks, I'm learning node and typescript the hard way :O

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
129 views
Welcome To Ask or Share your Answers For Others

1 Answer

You generally don't want to carry out an asynchronous operation in a constructor because it complicates creating an object and then knowing when the async operation is done or if it had an error because you need to allow the constructor to return the object, not a promise that would tell you when the async operation was done.

There are several possible design options:

Option #1: Don't do any async operation in the constructor. Then, add a new method with an appropriate name that does the async operation and returns a promise.

In your case, you could make the new method be scan() that returns a promise. Then, you'd use your object by creating it and then calling scan and then using the returned promise to know when the data is valid.

I don't know TypeScript myself, so I'll give a modified version of your code, but the concept is the same either way whether it's TypeScript or plain Javascript:

export class QueryAuthoriser {
  authPerms: [AuthPerms];

  constructor (data: string) {
  }

  scan () {
    return AuthPermsDDB.scan().execAsync().then ( (perms) => {
      perms.Items.forEach(element => {
        this.authPerms[element.name] = <AuthPerms> element.attrs
      })
    }).catch (err => {
      console.log ('%%%%%%%%%%%%%% Err loading authPerms: ', err)
    })
  }

}

// usage
let obj = new QueryAuthoriser(...);
obj.scan(...).then(() => {
    // the object is full initialized now and can be used here
}).catch(err => {
    // error here
})

Option #2: Initiate the async operation in the constructor and use a promise in the instance data for the caller to know when everything is done.

export class QueryAuthoriser {
  authPerms: [AuthPerms];

  constructor (data: string) {
    this.initialScan = AuthPermsDDB.scan().execAsync().then ( (perms) => {
      perms.Items.forEach(element => {
        this.authPerms[element.name] = <AuthPerms> element.attrs
      })
    }).catch (err => {
      console.log ('%%%%%%%%%%%%%% Err loading authPerms: ', err)
    })
  }

}

// usage
let obj = new QueryAuthoriser(...);
obj.initialScan.then(() => {
    // the object is full initialized now and can be used here
}).catch(err => {
    // error here
});

Option #3: Use a factory function that returns a promise that resolves to the object itself.

export createQueryAuthorizer;

function createQueryAuthorizer(...) {
    let obj = new QueryAuthorizer(...);
    return obj._scan(...).then(() => {
        // resolve with the object itself
        return obj;
    })
}

class QueryAuthoriser {
  authPerms: [AuthPerms];

  constructor (data: string) {
  }

  _scan () {
    return AuthPermsDDB.scan().execAsync().then ( (perms) => {
      perms.Items.forEach(element => {
        this.authPerms[element.name] = <AuthPerms> element.attrs
      })
    }).catch (err => {
      console.log ('%%%%%%%%%%%%%% Err loading authPerms: ', err)
    })
  }

}

// usage
createQueryAuthorizer(...).then(obj => {
    // the object is fully initialized now and can be used here
}).catch(err => {
    // error here
});

My preference is for option #3 for several reasons. It captures some shared code in the factory function that every caller has to do in the other schemes. It also prevents access to the object until it is properly initialized. The other two schemes just require documentation and programming discipline and can easily be misused.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...