node.jsreactjsexpressibm-watsonwatson-discovery

Variable undefined outside function scope despite being assigned?


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?


Solution

  • 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, []);
      }
    },