-1

I have the following use case:
Two visual grids are using two methods to load the data to display. These methods are automatically called by the grids and this part cannot be changed, it's by design:

    loadDataForGrid1 = (params: any): any => {
        return this.httpService.getData().then((response) => {
                return response.dataGrid1;
            }, (err) => {
        });
    }

    loadDataForGrid2 = (params: any): any => {
        return this.httpService.getData().then((response) => {
                return response.dataGrid2;
            }, (err) => {
        });
    }

Everything is working fine but my problem is performance. Since the getData method does an http request that is quite huge, calling it twice like Im doing right now is not acceptable. It there a way to solve this problem by doing only one call? Like caching the data so that they are reusable by the second call?

Im using typescript and angularjs

Edit:

Something like this would not work since the result would not be available when the grids load the data:

result: any;
// called at the beginning, for example contructor
loadData = (params: any): any => {
        return this.httpService.getData().then(result => {
            this.result = result;
        });
    }

loadDataForGrid1 = (params: any): any => {
        return this.result.gridGrid1;
    }

loadDataForGrid2 = (params: any): any => {
        return this.result.gridGrid2;
    }}

Using the answer suggested by @georgeawg generates the following javascript (which does 2 calls)

this.loadDataForGrid1 = function (params) {
    _this.promiseCache = _this.promiseCache || _this.httpService.getData();
    return _this.promiseCache.then(function (response) {
        return response.gridGrid1;
    }, function (err) {
    });
};
this.loadDataForGrid2 = function (params) {
    _this.promiseCache = _this.promiseCache || _this.httpService.getData();
    return _this.promiseCache.then(function (response) {
        return response.gridGrid2;
    }, function (err) {
    });
};
georgeawg
  • 48,608
  • 13
  • 72
  • 95
Bidou
  • 7,378
  • 9
  • 47
  • 70

4 Answers4

-1

You can always store the the data array in a variable on the page for SPA. If you want to use the data over different pages, you can use localStorage to 'cache' the data on the client-side.

localStorage.set("mydata", response.dataGrid1);
localStorage.get("mydata");

FYI, i does not seem you are using typescript, but rather native javascript :-)

-- Why don't you do something like this, or am i missing something?

$scope.gridData = {};

loadDataForGrid1 = (params: any): any => {
        return this.httpService.getData.then((response) => {
                $scope.gridData = response;
            }, (err) => {
        }).finally(function(){
console.log($scope.gridData.gridData1);
console.log($scope.gridData.gridData2);
});
    }
Verthosa
  • 1,671
  • 1
  • 15
  • 37
  • It is typescript but because this object is quite complex and just returned to the grid, it's not typed :) I'm not sure that your solution would work for me since I have to get the fresh data for each request: the user lands on my page with the two grids, and exactly here I only want one call (instead of two). Refreshing the page should do a new call. – Bidou Jul 26 '19 at 06:40
  • Why don't you just store the complete response in a variable in your scope then? I edited my reply – Verthosa Jul 26 '19 at 06:44
  • I'm not sure to understand your answers but it's probably because I do not know javascript enough. My two functions loadDataForGrid1 and loadDataForGrid02 are automatically called (probably in parallel) and I cannot know in which order ? So just doing `return $scope.gridData.dataGrid1` for the first one and `return $scope.gridData.dataGrid2` for the second will not work since the result will not be there at this time? – Bidou Jul 26 '19 at 06:52
  • In your example you are calling the same method twice (this.httpService.getData) but on each call you just get a different property (gridData1 and gridData2). Thus I would assume dat one call just gives both gridData properties at one time? – Verthosa Jul 26 '19 at 06:55
  • I added an example that would not work, maybe it helps to understand what I mean – Bidou Jul 26 '19 at 07:05
-1

What you can do is store the returned variable into a service variable and then do a check if it already exists.

dataGrid;

loadDataForGrid1 = (params: any): any => {
    if(!this.dataGrid) {
            return this.httpService.getData.then((response) => {
              this.dataGrid = response;
              return this.dataGrid.dataGrid1;
        }, (err) => {
      });
    }
    return this.dataGrid.dataGrid1;
}

loadDataForGrid2 = (params: any): any => {
    if(!this.dataGrid) {
       return this.httpService.getData().then((response) => {
              this.dataGrid = response;
              return this.dataGrid.dataGrid2;
        }, (err) => {
      });
    }
    return this.dataGrid.dataGrid2;
}

Something like this should work. Every time you call loadDataForGrid1 or loadDataForGrid2 you will first check if the data is already there - therefore you make an API call only once.

Dino
  • 7,779
  • 12
  • 46
  • 85
  • And how would you do for the second grid without calling the getData() method again? – Bidou Jul 26 '19 at 07:12
  • @Bidou Could you please be more specific? You have two services that call the same endpoint but you only want to call the endpoint once? Then why do you have two methods? And an answer to your question - then do the same check in loadDataForGrid2 (either the check for this.dataGrid1 or this.dataGrid2 - What ever you need to call) – Dino Jul 26 '19 at 07:22
  • But the loadDataForGrid2 is loading an other property of the (same) response: response.dataGrid2 – Bidou Jul 26 '19 at 07:34
  • Check the edit again. This approach will work or at least give you an idea how to do it. – Dino Jul 26 '19 at 07:43
  • 1
    I don't think that it will work because the dataGrid variable will take some time to be set (like one sec). So when you are testing `if(!this.dataGrid)` you will both time get a false result which will generate two calls. By the way, the downvote does not come from me.... – Bidou Jul 26 '19 at 07:51
-1

The solution is to cache the promise and re-use it:

var promiseCache;

this.loadDataForGrid1 = (params) => {
    promiseCache = promiseCache || this.httpService.getData();
    return promiseCache.then(result => {
        return result.gridGrid1;
    });
}

this.loadDataForGrid2 = (params) => {
    promiseCache = promiseCache || this.httpService.getData();
    return promiseCache.then(result => {
        return result.gridGrid2;
    });
}

Since the service immediately returns a promise, it avoids the race condition where the second XHR is started before the first XHR returns data from the server.


You mean that would be a javascript solution? But how to do it with typescript then?

JavaScript supports private variables.1

function MyClass() {
    var myPrivateVar = 3;

    this.doSomething = function() {
        return myPrivateVar++;        
    }
}

In TypeScript this would be expressed like so:

class MyClass {

    doSomething: () => number;

    constructor() {
        var myPrivateVar = 3;

        this.doSomething = function () {
            return myPrivateVar++;
        }
    }
}
georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • Looks promising... Just tested it but unfortunately its generates two calls. My understanding: Lets say that the first call is `loadDataFromGrid1`. the cache is null so its returns the promise from getData() and then the data are fetch when calling `then()`. `loadDataFromGrid2` is then called and this time the cache is set, but next line calls the `then()` method which execute the promise and generates the second call, right? Like you wrote, you cache the promise and not the result! So it is executed twice. – Bidou Jul 26 '19 at 08:23
  • The `.getData()` method should only be called once. The technique works in JavaScript. Not sure why it is not working here. It would be helpful to see how the TypeScript compiler translates this to JavaScript. – georgeawg Jul 26 '19 at 08:30
  • I added the generated javascript in the question – Bidou Jul 26 '19 at 08:42
  • Updated the question to use JavaScript. – georgeawg Jul 26 '19 at 08:55
  • You mean that would be a javascript solution? But how to do it with typescript then? – Bidou Jul 26 '19 at 09:01
  • A bit more help to write the new function would be appreciated. I tried to do something like this in the constructor `var promise = this.httpService.getData(); this.doSomething = () => { return promise;}` and then use doSomething in my two `loadDataForGridX` calls but its still generating two calls on the server – Bidou Jul 26 '19 at 09:56
-1

So, after many hours I came to the following solution. It's a bit a hack but it works.

In the initialization (constructor or so) Im loading the data:

this.httpService.getData().then((response) => {
            this.data1 = response.dataGrid1;
            this.data2 = response.dataGrid2;
            // other properties here...
            this.isReady= true;
        }, (err) => {
});

then I wrote an ugly wait method

    wait(): void {
        if (this.isReady) {
            return;
        } else {
            setTimeout(this.wait, 250);
        }
    }

Finally, my two methods look like this

    loadDataForGrid1 = (params: any): any => {
        this.wait();
        return this.$q.resolve(this.data1);
    }

    loadDataForGrid2 = (params: any): any => {
        this.wait();
        return this.$q.resolve(this.data2);
    }
Bidou
  • 7,378
  • 9
  • 47
  • 70