ChrisP
ChrisP

Reputation: 137

Node.js waiting for an async Redis hgetall call in a chain of functions

I'm still somewhat new to working with Node and def new to working asynchronously and with promises.

I have an application that is hitting a REST endpoint, then calling a chain of functions. The end of this chain is calling hgetall and I need to wait until I get the result and pass it back. I'm testing with Postman and I'm getting {} back instead of the id. I can console.log the id, so I know that this is because some of the code isn't waiting for the result of hgetall before continuing.

I'm using await to wait for the result of hgetall, but that's only working for the end of the chain. do I need to do this for the entire chain of functions, or is there a way to have everything wait for the result before continuing on? Here's the last bit of the logic chain:

Note: I've removed some of the logic from the below functions and renamed a few things to make it a bit easier to see the flow and whats going on with this particular issue. So, some of it may look a bit weird.

For this example, it will call GetProfileById().

 FindProfile(info) {
    var profile;
    var profileId = this.GenerateProfileIdkey(info); // Yes, this will always give me the correct key
    profile = this.GetProfileById(profileId);
    return profile;
}

This checks with the Redis exists, to verify if the key exists, then tries to get the id with that key. I am now aware that the Key() returns true instead of what Redis actually returns, but I'll fix that once I get this current issue resolved.

 GetProfileById(profileId) {
    if ((this.datastore.Key(profileId) === true) && (profileId != null)) {
        logger.info('GetProfileById ==> Profile found. Returning the profile');
        return this.datastore.GetId(profileId);

    } else {
        logger.info(`GetProfileById ==> No profile found with key ${profileId}`)
        return false;
    }
}

GetId() then calls the data_store to get the id. This is also where I started to use await and async to try and wait for the result to come through before proceeding. This part does wait for the result, but the functions prior to this don't seem to wait for this one to return anything. Also curious why it only returns the key and not the value, but when I print out the result in hgetall I get the key and value?

async GetId(key) {
var result = await this.store.RedisGetId(key);
    console.log('PDS ==> Here is the GetId result');
    console.log(result); // returns [ 'id' ]
    return result;
  }

and finally, we have the hgetall call. Again, new to promises and async, so this may not be the best way of handling this or right at all, but it is getting the result and waiting for the result before it returns anything

 async RedisGetId(key) {
      var returnVal;
      var values;
      return new Promise((resolve, reject) => {
          client.hgetall(key, (err, object) => {
            if (err) {
              reject(err);
            } else {
              resolve(Object.keys(object));
              console.log(object); // returns {id: 'xxxxxxxxxxxxxx'}
              return object;
            }
          });
        });
      }

Am I going to need to async every single function that could potentially end up making a Redis call, or is there a way to make the app wait for the Redis call to return something, then continue on?

Upvotes: 1

Views: 1856

Answers (1)

Andrew Eisenberg
Andrew Eisenberg

Reputation: 28757

Short answer is "Yes". In general, if a call makes an asynchronous request and you need to await the answer, you will need to do something to wait for it.

Sometimes, you can get smart and issue multiple calls at once and await all of them in parallel using Promise.all.

However, it looks like in your case your workflow is synchronous, so you will need to await each step individually. This can get ugly, so for redis I typically use something like promisify and make it easier to use native promises with redis. There is even an example on how to do this in the redis docs:

const {promisify} = require('util');
const getAsync = promisify(client.get).bind(client);
...
const fooVal = await getAsync('foo');

Makes your code much nicer.

Upvotes: 1

Related Questions