schenker
schenker

Reputation: 953

How to check if variable is function in a javascript class method without using eval

i have created this class to check if the function has been loaded. It works fine, except that I want to avoid using eval to evaluate the function name from a variable. I appreciate any help with this. Thanks.

class scriptLoader {

constructor(fn, max_try) {        
    this.fn = fn; //the func name being passed as a string
    this.count = 0; //init count start value                
    this.max_try = max_try; //the max times to try
}



waitForScriptToLoad() {           
      'use strict';        
        let my_function = eval(this.fn); //this evaluate the string, i.e 'myfunc' to myfunc()                       
      if(typeof my_function === "function") {                        
        my_function();
    } else {                                    
        if(this.count<this.max_try) {
            var that = this;
            setTimeout(function() {that.count++; that.waitForScriptToLoad();},100); //wait 100ms and try again            
        } else {                
            return false;                
        }            
    }
}

}

I instantiate the class like so:

loadData = new scriptLoader('Worker.fetch_form_data', 3);
loadData.waitForScriptToLoad();

Upvotes: 0

Views: 51

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 370729

Assuming that the script you're trying to detect will be located on the global object, split the string by .s and check to see if the nested property exists on the global object:

class scriptLoader {

  constructor(propsStr, max_try) {
    this.propsStr = propsStr; //the func name being passed as a string
    this.count = 0; //init count start value                
    this.max_try = max_try; //the max times to try
  }
  waitForScriptToLoad() {
    'use strict';
    const possibleFn = this.propsStr.split('.').reduce((a, prop) => (a?.[prop]), globalThis);
    if (typeof possibleFn === "function") {
      possibleFn();
    } else {
      if (this.count < this.max_try) {
        console.log('retrying');
        setTimeout(() => {
          this.count++;
          this.waitForScriptToLoad();
        }, 100); //wait 100ms and try again            
      }
    }
  }
}

// Dynamic loading of script:
setTimeout(() => {
  window.obj = {};
}, 50);
setTimeout(() => {
  window.obj.nested = {
    fn: () => console.log('fn')
  };
}, 150);


const loadData = new scriptLoader('obj.nested.fn', 3);
loadData.waitForScriptToLoad();

Also note:

  • If using ES2015+ syntax (which you are, and you should be!), best to use const rather than let if you don't require reassignment - avoid var
  • In ES2015+, better to use an arrow function than the that = this antipattern
  • Remember to declare your variables when using them - your loadData is implicitly global since you didn't declare it (consider enabling strict mode at the top level)

Upvotes: 1

Related Questions