Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
142 views
in Technique[技术] by (71.8m points)

node.js - Node code not blocking?

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

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

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...