-1

I have the following functions that I'm trying to call from a .then()

   public waitFunction(): any {
        setTimeout(function() {
            this.getMap1();
        }, 1000);
        return;
    }

    public getMap1(): any {
        this.autoSchemaRecords = null;
        this._clientFileMappingService.getAllById(this._clientFileId).subscribe(
            r => {
                this.autoSchemaRecords = r;
            },
            e => {
                this.showLoader = false;
                throw e;
            },
            () => {
                this.showLoader = false;
            }
        );
        return;
    }

When I try to call the waitFunction() from the .then() below I get the following error:

this.getMap1 is not a function

const result = this._clientFileMappingService
    .fileUpload(autoSchema, this._clientFileId)
    .then(this.hideModal(), this.waitFunction())
    .catch(err => {
        throw err;
    });

This is confusing me because everything but the services are in the same component.

I am currently using a promise in my .fileUpload method for various reasons. If that function was an observable, I don't think I would be having this issue. However, I can't change it at this time.

Can anyone lead me in the right direction to be able to wait 1 second to repopulate autoSchemeaRecords collection?

webdad3
  • 8,436
  • 28
  • 114
  • 206
  • 2
    `this` isn't what you think it is in that location. – Kevin B Oct 31 '19 at 18:04
  • Try using an arrow function in setTimeout – Pavan Oct 31 '19 at 18:04
  • 1
    I'd suggest reading this [Six ways to control this in a function call](https://stackoverflow.com/questions/28016664/when-you-pass-this-as-an-argument/28016676#28016676) – jfriend00 Oct 31 '19 at 18:45
  • 1
    And, I hope you realize that `waitFunction()` doesn't actually "wait" for anything. `setTimeout()` is non-blocking so it schedules the timer and returns immediately and the rest of your code continues to run. Calling it `runAfterDelay()` is more of a description of what it actually does. – jfriend00 Oct 31 '19 at 18:46

2 Answers2

1

That "arrow syntax" or some call it the "fat arrow syntax" for declaring a function was added to the language specifically to solve this type of problem.

All function calls to functions defined with the regular function syntax (without the fat arrow syntax) cause a new value of this to be set according to how the function is called. You can see the various way that this is set or controlled here The six ways to control the value of this in a function call.

So, the callback you had to your setTimeout() will cause a different value of this inside that callback. In this case it will be either the global value or undefined (if running in strict mode) since it will be "reset" to the default value when setTimeout() calls the callback.

Assuming that waitFunction() is a method that has the correct this itself (which is what your code shows) and is called like obj.waitFunction() or this.waitFunction(), then you can switch to a fat arrow function, and it will preserve the lexical value of this rather than set a new one:

public waitFunction(): any {
    setTimeout(() => {
         // will have the same value of this that was
         // present before the setTimeout() was executed
         // which is what you want
         this.getMap1();
    }, 1000);
    return;
}

Or, a more compact version (single statement arrow functions can skip the braces):

public waitFunction(): any {
    setTimeout(() => this.getMap1(), 1000);
}

The lexical value of this is what the value of this was naturally BEFORE the setTimeout() function call when that code ran. In this case that is the value you want inside the callback so the fat arrow syntax is perfect for this situation because it preserves the value of this that you want.

So, the fat arrow syntax is basically two things:

  1. A shorter function declaration syntax
  2. A means of preserving the lexical value of this inside the function

They are perfect for inline function declarations when you want to preserve the value of this

You can read more about them here on MDN.


Also, keep in mind that waitFunction() would better be named delayFunction() because it doesn't actually "wait" for anything. setTimeout() is non-blocking. It will call this.getMap1() after a delay, but it returns immediately and your other code continues to run. So, it's not really a "wait". Just wanted to make sure you weren't confused about that.

jfriend00
  • 580,699
  • 78
  • 809
  • 825
0

setTimeout is from vanilla JS, so according to your code

   public waitFunction(): any {
    let context=this;
    setTimeout(function() {
        context.getMap1();
    }, 1000);
    return;
}

public getMap1(): any {
    this.autoSchemaRecords = null;
    this._clientFileMappingService.getAllById(this._clientFileId).subscribe(
        r => {
            this.autoSchemaRecords = r;
        },
        e => {
            this.showLoader = false;
            throw e;
        },
        () => {
            this.showLoader = false;
        }
    );
    return;
}

for easy understanding, pass the context as a variable, otherwise setTimeout will try to find the context from a JS environment.

Viroj Fernando
  • 435
  • 4
  • 8
  • @jfriend00 - So based off of your comment I tried using a fat arrow and it worked. Why does the fat arrow work? That seems like some voodoo magic. – webdad3 Nov 14 '19 at 15:33
  • @webdad3 - I added an answer that explains the use of the fat arrow syntax for this situation. I have no idea why it was downvoted. It works because this is one of the specific things that the arrow syntax was designed to do. – jfriend00 Nov 14 '19 at 16:29
  • @jfriend00 - I upvoted it for the effort :) - Thank you for the clarification. This was the 1st time that the different scopes of 'this' has happened to me. I appreciate you challenging my original thought (although it also worked). When I tried using the Fat Arrow and it worked, I was dumbstruck... I think I said it was voodoo... Your answer below is a very good explanation (IMHO) – webdad3 Nov 14 '19 at 17:51