Nishant Sharma
Nishant Sharma

Reputation: 25

Node js, function doesn't wait for response

I'm new to node js. Logically, await should wait for the function to complete and then next line of code should run, but in my function, it doesn't wait for process (2) to finish even after using await. What am I missing?

const getDefaultTemplates = async (token) => {
  const emailertoken = process.env.EMAILER_AUTH_TOKEN;
  const key = 'defaultTemplates';
  // This should run first
  console.log('hi');
  let defaultTemplatesVar = '';
  let fromCache = 0;

  // This should run second. Need response from redisClient to return parent function
  const defaultTemplates = await redisClient.get(key, (err, data) => {
    if (err) {
      console.log(err);
    }
    // If data is found in cache
    if (data != null) {
      console.log('from cache');
      defaultTemplatesVar = JSON.parse(data);
      fromCache = 1;
      console.log('defaultTemplatesVar1 = ', defaultTemplatesVar);
    }
    return defaultTemplatesVar;
  });

  console.log('defaultTemplatesVar2 = ', defaultTemplatesVar);
  console.log('fromCache = ', fromCache);
  if (fromCache === 0) {
    // If data is not found in cache, call api
    // console.log('from api');
    try {
      const response = await axios.get(`${process.env.EMAILER_API_URL}/get-system-templates`, {
        headers: {
          Authorization: `bearer ${emailertoken}`,
        },
      });
      console.log('from data');
      redisClient.setex(key, 3600, JSON.stringify(response.data._embedded.get_system_templates));
      defaultTemplatesVar = response.data._embedded.get_system_templates;
      console.log('defaultTemplatesVar3 = ', defaultTemplatesVar);
    } catch (error) {
      console.error(error);
    }
  }

  // This should run at last, to return value to parent function getDefaultTemplates()
  console.log('bye');
  console.log('defaultTemplatesVar4 = ', defaultTemplatesVar);
  return defaultTemplates;
};


OUTPUT ->

hi
defaultTemplatesVar2 =  
fromCache =  0
from cache
defaultTemplatesVar1 =  [ { name: 'versafix-1', description: 'The versatile template' },
  { name: 'givecentral', description: 'The Givecentral Template' } ]
from data
defaultTemplatesVar3 =  [ { name: 'versafix-1', description: 'The versatile template' },
  { name: 'givecentral', description: 'The Givecentral Template' } ]
bye
defaultTemplatesVar4 =  [ { name: 'versafix-1', description: 'The versatile template' },
  { name: 'givecentral', description: 'The Givecentral Template' } ]

Output should be in series of 1,2,3,4.

Upvotes: 1

Views: 1242

Answers (2)

Nishant Sharma
Nishant Sharma

Reputation: 25

My issue was solved from Allen's Answer.

Only thing I changed in Allen's suggestion was

const data = await redisGet(key);

Instead of

const data = await redisClient.get(key);

because the later one returned 'true' instead of actual values.

const redisGet = (key) => new Promise((resolve, reject) => {
  redisClient.get(key, (err, data) => {
    if (err) return reject(err);
    return resolve(data);
  });
});

// Api,Auth Done
const getDefaultTemplates = async (token) => {
  const emailertoken = process.env.EMAILER_AUTH_TOKEN;
  const key = 'defaultTemplates';
  // This should run first
  console.log('hi');
  let defaultTemplatesVar = '';
  let fromCache = 0;

  // This should run second. Need response from redisClient to return parent function
  try {
    const data = await redisGet(key);

    if (data != null) {
      console.log('from cache');
      defaultTemplatesVar = JSON.parse(data);
      fromCache = 1;
      console.log('defaultTemplatesVar1 = ', defaultTemplatesVar);
    }
    const defaultTemplates = defaultTemplatesVar;
  } catch (err) {
    console.error(err);
  }

  console.log('defaultTemplatesVar2 = ', defaultTemplatesVar);
  console.log('fromCache = ', fromCache);
  if (fromCache === 0) {
    // If data is not found in cache, call api
    // console.log('from api');
    try {
      const response = await axios.get(`${process.env.EMAILER_API_URL}/get-system-templates`, {
        headers: {
          Authorization: `bearer ${emailertoken}`,
        },
      });
      console.log('from data');
      redisClient.setex(key, 3600, JSON.stringify(response.data._embedded.get_system_templates));
      defaultTemplatesVar = response.data._embedded.get_system_templates;
      console.log('defaultTemplatesVar3 = ', defaultTemplatesVar);
    } catch (error) {
      console.error(error);
    }
  }

  // This should run at last, to return value to parent function getDefaultTemplates()
  console.log('bye');
  console.log('defaultTemplatesVar4 = ', defaultTemplatesVar);
  return defaultTemplatesVar;
};

OUTPUT 1 (When data comes from the API)

hi
defaultTemplatesVar2 = 
fromCache =  0
from data
defaultTemplatesVar3 =  [ { name: 'versafix-1', description: 'The versatile template' },
  { name: 'givecentral', description: 'The Givecentral Template' } ]
bye
defaultTemplatesVar4 =  [ { name: 'versafix-1', description: 'The versatile template' },
  { name: 'givecentral', description: 'The Givecentral Template' } ]

OUTPUT 2 (When Data comes from Cache)

hi
from cache
defaultTemplatesVar1 =  [ { name: 'versafix-1', description: 'The versatile template' },
  { name: 'givecentral', description: 'The Givecentral Template' } ]
defaultTemplatesVar2 =  [ { name: 'versafix-1', description: 'The versatile template' },
  { name: 'givecentral', description: 'The Givecentral Template' } ]
fromCache =  1
bye
defaultTemplatesVar4 =  [ { name: 'versafix-1', description: 'The versatile template' },
  { name: 'givecentral', description: 'The Givecentral Template' } ]

Upvotes: 0

Allen Wong
Allen Wong

Reputation: 1433

Callback function can not be await, To turn callback fn to Promise fn, check out https://www.npmjs.com/package/redis#promises .

If you are using other packages, you actually can turn callback to promise by

const redisGet = (key) => new Promise((resolve, reject) => {
  redisClient.get(key, (err, data) => {
    if (err) return reject(err);
    return resolve(data);
  });
});

// So you can do
const data = await redisGet(key);
// or
redisGet(key).then((data) => { ... }).catch((err) => { ... });

so what you should do

try {
  ...

  const data = await redisClient.get(key);

  if (data != null) {
    console.log('from cache');
    defaultTemplatesVar = JSON.parse(data);
    fromCache = 1;
    console.log('defaultTemplatesVar1 = ', defaultTemplatesVar);
  }
  const defaultTemplates = defaultTemplatesVar;
  ...
} catch (err) {
  console.error(err);
}

Simple tutorial about Callback vs Promise

Upvotes: 1

Related Questions