1

Problem

I'm having an issue with a MERM application whereby a variable which is declared in the body of the whole function is assigned to in a sub function, however, whenever I try to manipulate/display this variable just at the end of the whole function, it returns an "undefined".

Code

getSimilarCasesFromID: function(req, res, next) {
    let queryString = "id::"+req.params.id; 
    let params = {
        'query': queryString,
        'environment_id':environmentId,
        'collection_id': collectionId,
        'configuration_id': configurationId,
        return: 'enriched_text'
    }

    let filterStrArr = [];
    const FILTER_CONCEPT = "enriched_text.concepts.text:";
    let filterStr ="";

    discovery.query(params, (error, results) => {
        if (error) {
            next(false, err, []);
        } else {
            let conceptSize = results.results[0].enriched_text.concepts.length;
            let concepts = {};
            for (let i = 0; i < conceptSize; i++) {
                concepts[i] = { 
                    text: results.results[0].enriched_text.concepts[i].text,
                    relevance: results.results[0].enriched_text.concepts[i].relevance
                 };

                filterStrArr[i] = FILTER_CONCEPT + concepts[i].text;
            }

            filterStr = filterStrArr.join(",");
            console.log(filterStr);
            //1. Works and displays---------------
        }
    });

    console.log("FullString (2.)"+filterStr);
    //2. Undefined????????????????????------------

    next(true, [], []);
},

I refer to lines (1.) and (2.). I cannot tell whether I have missed something and made a silly, menial error.

Output on the server: enter image description here

Interestingly, as you see in Figure 1, FullString (2.) appears before line (1.). Could this be due to the response time from Watson Discovery? Bearing in mind that the service is located in Sydney, Australia? And if so, has anyone else had any experience with this?

EnglischerZauberer
  • 237
  • 1
  • 2
  • 11

2 Answers2

1

discovery.query() takes two properties,
1 - param
2 - function (also known as a callback)

discover.query() gets executed, but returns before the callback is executed,
next console.log("FullString (2.)"+filterStr); is executed
Finally, the callback is executed.

You need to take your desired action(s) in your callback.

This is a pain, but is how JavaScript works. More accurately, how discovery.query() works. Worse is when you have nested callbacks, it can get very confusing and has a name: "callback hell". Modern solutions to this problem is promises and async await.

Good luck

Mke Spa Guy
  • 793
  • 4
  • 13
1

The discovery.query is an asynchronous function, so the variable filterStr won't be defined until it has reached the callback. You'll need to use filterStr within the discovery.query's callback or use async/await within a try/catch block to return the results.

Asynchronous functions work by allowing it to be executed without interrupting the thread. So that's why the console.log(2.) gets executed, then sometime later, the console.log(1.) gets executed within the callback (due to the function being asynchronous, it takes some time to reach the callback). You're trying to do things synchronously, and to do so, you'll need to refactor your code. Click here for more information about asynchronous and synchronous execution.

Within callback:

getSimilarCasesFromID: function(req, res, next) {
  const queryString = "id::"+req.params.id; 
  const params = {
    'query': queryString,
    'environment_id':environmentId,
    'collection_id': collectionId,
    'configuration_id': configurationId,
    return: 'enriched_text'
  };

  const filterStrArr = [];

  const FILTER_CONCEPT = "enriched_text.concepts.text:";

  discovery.query(params, (error, results) => {
    if (error) {
      next(false, err, []);
    } else {
      let filterStr ="";

      const conceptSize = results.results[0].enriched_text.concepts.length;

      let concepts = {};
      for (let i = 0; i < conceptSize; i++) {
        concepts[i] = { 
          text: results.results[0].enriched_text.concepts[i].text,
          relevance: results.results[0].enriched_text.concepts[i].relevance
        };

        filterStrArr[i].push(FILTER_CONCEPT + concepts[i].text);
      }

      filterStr = filterStrArr.join(",");
      console.log(filterStr);

      // utilize filterStr here

      next(true, [], []);
    }
  });
},

With async/await:

getSimilarCasesFromID: async function(req, res, next) {
  const queryString = "id::"+req.params.id; 
  const params = {
    'query': queryString,
    'environment_id':environmentId,
    'collection_id': collectionId,
    'configuration_id': configurationId,
    return: 'enriched_text'
  }

  const filterStrArr = [];

  const FILTER_CONCEPT = "enriched_text.concepts.text:";

  let filterStr ="";

  try {
    const results = await discovery.query(params);

    const conceptSize = results.results[0].enriched_text.concepts.length;

    let concepts = {};
    for (let i = 0; i < conceptSize; i++) {
      concepts[i] = { 
        text: results.results[0].enriched_text.concepts[i].text,
        relevance: results.results[0].enriched_text.concepts[i].relevance
      };

      filterStrArr[i].push(FILTER_CONCEPT + concepts[i].text);
    }

    filterStr = filterStrArr.join(",");
    console.log(filterStr);

    // utilize filterStr here

    next(true, [], []);       

  } catch(err) {
     next(false, err, []);
  }
},
Matt Carlotta
  • 18,972
  • 4
  • 39
  • 51
  • Thanks for your help, Matt! I must confess that I was initially confused with the terminology pertaining to "synchronous" and "asynchronous" functions. Your explanation was extremely helpful. – EnglischerZauberer Mar 05 '19 at 14:51