1

I am quiet new in TypeScript and JavaScript and I have a problem. I have function that I copy-pasted from another app, but I slightly modified it:

myServer.prototype.filterList = function(_data, filterModel) {
  const resultOfFilter = [];
  function filterData(filtModel) {
    let httpParams = new HttpParams();
    for (const f in filtModel) {
      // some code
    }
    const httpOptions = {
      params: httpParams,
    };
    return that.httpClient.get(`${baseUrl}${tableName}`, httpOptions);
  }

  filterData(filterModel).subscribe((filteredData: any[] ) => {
    filteredData.forEach((item) => {
      resultOfFilter.push(item);
    });
  });

  return resultOfFilter;
};

This function returns an array like that: enter image description here

I pass this array to another function where I want to iterate over its items. I tried following approaches:

a)

resultOfFilter.forEach(i => {
    console.log('I:', i);
  });

b)

for (let i; i <= resultOfFilter.length; i++){console.log('I:', resultOfFilter[i]);}

c)

Object.keys(resultOfFilter).forEach(i => {
    console.log('I:', i);
  });

But all these functions did not work for me. And I can't understand why. I can display the whole array, but I can't get its items for some reason. Please advise how can I get each item of that array. P.S.: When I use typeof resultOfFilter I get object.

Artsiom
  • 197
  • 1
  • 9
  • a and b should work , can you please tell what error are you facing while executing a and b?? – Shlok Nangia May 09 '20 at 11:28
  • 1
    Please add a [mcve] or at least describe in more detail what problems you have. _"all these functions did not work for me"_ is meaningless without more input (like an [mcve]) – Andreas May 09 '20 at 11:31
  • 2
    Duplicate of [Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference](https://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron) – Guy Incognito May 09 '20 at 11:31
  • 1
    See also https://stackoverflow.com/questions/4057440/is-chromes-javascript-console-lazy-about-evaluating-arrays – the array is empty when you log it (see the `[]` on the first line), but when you open it to check its contents, the Ajax call has returned and the array has been filled. – Guy Incognito May 09 '20 at 11:32
  • 1
    Asynchronous problem. You return the array before it is being filled. – Random May 09 '20 at 12:26

2 Answers2

2

you have an asynchronous problem. you are returning resultOfFilter but your get request in that time is processing. you need to wait until your request will be completed, after that return resultOfFilter

myServer.prototype.filterList = async function(_data, filterModel) {
  const resultOfFilter = [];
  function filterData(filtModel) {
    let httpParams = new HttpParams();
    for (const f in filtModel) {
      // some code
    }
    const httpOptions = {
      params: httpParams,
    };
    return that.httpClient.get(`${baseUrl}${tableName}`, httpOptions);
  }

  const filteredData = await filterData(filterModel).toPromise();

  filteredData.forEach((item) => {
      resultOfFilter.push(item);
  });


  return resultOfFilter;
};

Usage example

import { Component, OnInit } from '@angular/core';
import { myServer } from 'path-to-service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  constructor(private myServer: myServer) { }

  ngOnInit() {
     this.getResultOfFilter();
  }

  async getResultOfFilter() {
   let result = await this.myServer.filterList(data, filterModel);
   console.log(result, 'getResultOfFilter');
  }
}

or

import { map } from 'rxjs/operators';

myServer.prototype.filterList = function(_data, filterModel) {
  const resultOfFilter = [];
  function filterData(filtModel) {
    let httpParams = new HttpParams();
    for (const f in filtModel) {
      // some code
    }
    const httpOptions = {
      params: httpParams,
    };
    return that.httpClient.get(`${baseUrl}${tableName}`, httpOptions);
  }

  return filterData(filterModel).pipe(
    // map operator should be imported from `rxjs/operators`
    map(filteredData => {
      filteredData.forEach((item) => {
        resultOfFilter.push(item);
      });

      return resultOfFilter;
    })
  );


};

Usage example

import { Component, OnInit } from '@angular/core';
import { myServer } from 'path-to-service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  constructor(private myServer: myServer) { }

  ngOnInit() {
     this.getResultOfFilter();
  }

  getResultOfFilter() {
    this.myServer.filterList(data, filterModel).subscribe(result => {
      console.log(result, 'getResultOfFilter');
    });
  }

}
Ashot Aleqsanyan
  • 4,252
  • 1
  • 15
  • 27
-1

use async and await

myServer.prototype.filterList = async function(_data, filterModel) {

  const filterData = async function (filtModel) {
    let httpParams = new HttpParams();
    for (const f in filtModel) {
      // some code
    }
    const httpOptions = {
      params: httpParams,
    };
    return that.httpClient.get(`${baseUrl}${tableName}`, httpOptions);
  }

  await (await filterData(filterModel)).pipe(map((filteredData: any[] ) => {
    const resultOfFilter = [];
    filteredData.forEach((item) => {
      resultOfFilter.push(item);
    });
    return resultOfFilter;
  }));

};

The httpClient.get is an asynchronous operation and takes some time to fetch the data. Mark all asynchronous functions with async and use await when calling to asynchronous functions to return promises.

Hope this helps!

Update: This function returns an observable which can be subscribed to get the value.

Another option is to replace await (await filterData(filterModel)).pipe(map(...)) with await (await filterData(filterModel)).toPromise().then(...) to return a Promise instead.

T. Sunil Rao
  • 1,167
  • 5
  • 14
  • You can't use the `await` keyword on observables (what you get with `this.httpClient.get(...)`) or subscriptions (what you get with `.subscribe(...)`, only promises. So this code won't work. – ShamPooSham May 09 '20 at 13:47
  • @ShamPooSham `await` will work with any data type even with observables. It returns Promise> which works as well. You also have the option to convert observables to promises using .toPromise(). Please check and let me know. – T. Sunil Rao May 09 '20 at 14:12
  • awaiting a `Promise>` will return you the Observable, not the value. This may not give you syntax errors, but it won't do what you want it to do – ShamPooSham May 09 '20 at 15:15
  • "The await operator is used to wait for a Promise". https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await – ShamPooSham May 09 '20 at 15:16
  • But yes, you can convert the observables to promises using `toPromise()`. However, if you're using rxjs you should learn how to use that instead of converting observables to promises IMO – ShamPooSham May 09 '20 at 15:18
  • awaiting a `Promise>` will return the Observable, but `await (await filterData(filterModel)).subscribe(...` resolves the promise first and then uses the observable. and then recreates a promise for it and returns back. – T. Sunil Rao May 09 '20 at 15:23
  • As per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await the syntax is `await expression` where expression is a Promise or any value to wait for. – T. Sunil Rao May 09 '20 at 15:28
  • Typescript and RxJs provides us multiple ways to do the same thing. It's personal choice which to choose IMO – T. Sunil Rao May 09 '20 at 15:30
  • I made an example in stackblitz, where instead of doing API calls I just create an observable that waits some ms before emitting the same value. It doesn't work. https://stackblitz.com/edit/rxjs-r2etn8 – ShamPooSham May 09 '20 at 15:41
  • The syntax is `await expression`, yes. But the returned value is "the value itself if it's not a Promise". That means the returned value will be the observable if you await the observable and it will be the subscription if you await the subscription. It won't do anything in those cases. – ShamPooSham May 09 '20 at 15:44
  • Thanks for pointing out the return of the subscription. It should have been mapped to return an observable instead. I'm updating my reply – T. Sunil Rao May 09 '20 at 15:54
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/213496/discussion-between-t-sunil-rao-and-shampoosham). – T. Sunil Rao May 09 '20 at 15:58