Unable to store the output of an asynchronous function in an object

When I manage to fix it, I'll be home. The current issue is saving the result from an async function. If I do a console.log after that function, I receive the translated value. However, a few moments later when I check how my verbs look like after being packed into an object, I receive the non-translated value. Does anyone know why?

private translate(file: any) {

    const keys  = Object.keys(file)
    const values = Object.values(file) // [!] - English Value at start, it's ok

    values.forEach( async (value, index) => {
      if( typeof(value) === 'object') {
        value = this.translate(value)           
      } else {
        const translateResult = await this.GetTranslatedByResstring(keys[index], 1045).toPromise()
        value = translateResult.getPhrase() 
        console.log(value)  //  [!] <- Polish Value, it's ok
      }
    })

    const zipObj = xs => ys => xs.reduce( (obj, x, i) => ({ ...obj, [x]: ys[i] }), {})
    const obj = zipObj (keys) (values) 
    console.log(obj) //  [!] <- English Value, it's not ok, i need Polish value inside
    return obj
  }

#Update 1

The location where translate is called:

public async getTranslatedJson(
    sourceFile: File,
    originLanguage: Language,
    destinatonLanguage: Language
  ): Promise<string> {
    const file = await this.getFile(sourceFile)
    const parsedFile = JSON.parse(file)
    const translatedFile = this.translate(parsedFile)
    return null
  }

#Update 2

My getFile function:

private getFile(
    sourceFile: File
  ): Promise<string> {
    return new Promise((resolve) => {
      const file = sourceFile[0]
      const fileReader = new FileReader();
      fileReader.onloadend = (e) => { 
        const testResult = fileReader.result.toString();
        resolve(testResult);
      }
      fileReader.readAsText(file, "UTF-8");
    });
  }

Answer №1

Expanding on my earlier comment, there is no necessity to convert the observable into a promise in this scenario. Instead, you can opt to return a RxJS forkJoin function that simultaneously triggers multiple observables and subscribe to it when the response is needed.

In addition, rather than utilizing Object.keys, Object.values, and Array#reduce, you could make use of Object.entries and Object.fromEntries.

You could try the following:

import { of, forkJoin } from 'rxjs';
import { map } from 'rxjs/operators';

private translate(file: any): Observable<any> {                // <-- specify `Observable` as the return type
  return forkJoin(                                             // <-- utilize `forkJoin` to trigger sources concurrently
    Object.entries(file).map(([key, value]) => {               // <-- make use of `Object.entries` with `Array#map`
      if (typeof (value) === 'object')
        return of([key, this.translate(value)]);               // <-- employ `of` to convert the value to an observable
      return this.GetTranslatedByResstring(value, 1045).pipe(
        map((res: any) => ([key, res.getPhrase()]))            // <-- map to the desired format [key, value]
      );
    })
  ).pipe(
    map((res: any) => Object.fromEntries(res))                 // <-- convert back to an object
  );
}

Now, since the function returns an observable, make sure to subscribe to it where the response is required.

For example:

ngOnInit() {
  this.translate(file).subscribe({
    next: (value: any) => {
      // Place any statements dependent on the response here
    },
    error: (error: any) => {
      // Handle errors
    }
  })
}

Update 1

Revisiting my previous point, converting the observable into a promise seems unnecessary. Therefore, avoid using toPromise() in the this.getFile() function and return the observable directly. In the getTranslatedJson() function, consider using a higher order mapping operator such as switchMap to map from one observable to another.

public getTranslatedJson(
  sourceFile: File,
  originLanguage: Language,
  destinatonLanguage: Language
): Observable<any> {
  return this.getFile(sourceFile).pipe(
    switchMap((file: any) => 
      this.translate(JSON.parse(file))
    )
  );
}

In this instance, you would subscribe to the getTranslatedJson() function for obtaining its response.

ngOnInit() {
  this.getTranslatedJson(file, origLang, destLang).subscribe({
    next: (value: any) => {
      // Place any statements dependent on the response here
    },
    error: (error: any) => {
      // Handle errors
    }
  })
}

Update 2

If for some reason Object.entries and Object.fromEntries are unavailable, you can revert to using .values, .keys, and Array#reduce as originally intended.

private translate(file: any): Observable<any> {            
return forkJoin(                                            
Object.values(file).map((value: any) => {                   
if (typeof (value) === 'object')
return of(this.translate(value));                    
return this.GetTranslatedByResstring(value, 1045).pipe(
map((res: any) => res.getPhrase())                     
);
})
).pipe(
map((res: any) =>                                          
Object.keys(file).reduce((acc, curr, i) => 
({ ...acc, [curr]: res[i] }), 
Object.create(null)
);
)
);
}

Update 3

Many solutions to simple tasks can be derived by building upon existing answers.

// credit: https://stackoverflow.com/a/48802804/6513921

private getFile(
sourceFile: File
): Observable<any> {
const file = sourceFile[0];
const fileReader = new FileReader();

return Observable.create((observer: Subscriber<any>): void => {
fileReader.onloadend = ((ev: any): void => {
observer.next(fileReader.result.toString());
observer.complete();
});

fileReader.onerror = (error: any): void => {
observer.error(error);
}
});

fileReader.readAsText(file, "UTF-8");
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The object is not a valid function

Within this class object, I have an instance of a class that I am unable to call its functions within. Despite the IDE allowing me to call the getPoistionDiagram function: export class NodeW { childrenIds: string[]; diagram?: { coordinates: { ...

Angular 6: Unable to resolve parameters for Component: (?)

We have been using Angular v6.0.0-beta.3 for a while now, but we recently attempted to upgrade to version 6.1.3. Unfortunately, I faced issues upgrading with Angular schematics due to lack of support for external registries like Artifactory. As a result, ...

Enable various routes simultaneously within Angular 2 and above

I want to be able to simultaneously open two child routes in named router outlets. This way, I can ensure that router outlet A always displays a specific component such as login. The second router outlet B would then contain a reusable component that proc ...

The modal stubbornly refuses to close

The main component responsible for initiating the process is /new-order. Upon clicking on the confirm button, a modal window appears. <div class="col-12"> <button type="button" class="btn btn-primary m-1" (click)=& ...

What causes an array of type `never[] & number[]` to be generated when using `Object.assign`?

Take a look at this code snippet: const arr = [1,2,3] const res1 = arr.slice() const res2 = Object.assign([],arr) When using arr.slice() to create a shallow clone, the resulting array res1 will have a type of number[], just like the original arr. However, ...

Publish the file on Angular 7 route for accessibility

I'm currently stuck on an issue that I can't seem to figure out. I'm attempting to link my website to Apple Pay through Stripe's PaymentRequest feature. However, I'm running into difficulties with one of the requirements they have ...

Encapsulation in Angular Material: mat-error

Considering encapsulating the mat-error within its own component due to the lengthy text. The proposed solution is: <form [formGroup]="form"> <mat-form-field> <input matInput formControlName="title" placeholde ...

What is the best way to display my images within Material UI Cards using React with Typescript?

I'm currently faced with a challenge of rendering images within Material UI's cards. I am successfully passing props to each card from a constants.tsx file where the heading, description, and bodyHeader are all properly read, but for some reason, ...

How can I transform this imperative reducer into a more declarative format using Ramda?

I am currently working with a reducer function that aggregates values in a specific way. The first argument is the aggregated value, while the second argument represents the next value. This function reduces over the same reaction argument, aggregating th ...

Preserving the chosen selection from a drop-down menu

I am working on implementing a dropdown menu with various options. My goal is to ensure that the selected option remains at the top of the dropdown even if the user navigates away from the component and then returns. Currently, when I select an option and ...

adding new data rows to an existing data list using Angular

I am currently working on displaying data from a backend system. The data is coming from a table that receives new rows within a specific time frame. To retrieve these new rows, I have set up a mechanism using an Angular timer. My query pertains to the tem ...

The placeholder within my input moves up and down when switching the input type from password to text

Currently, I am encountering an issue with the styling of a standard input element in React. Specifically, the placeholder text moves up and down by about 2px when viewed on Chrome, while there are no problems on Safari. How can I go about resolving this i ...

Attempting to iterate through classes with for loops in Python version 3.5

Looking to create a for loop that iterates through a range and generates classes with unique names. Wondering how this can be achieved and what naming convention should be used. Here's an example of my current code: for i in range(6): class Name( ...

What is the most efficient method for parsing nested objects in AngularJS?

Currently, I am faced with the challenge of utilizing angularjs to parse a complex nested data structure retrieved from a remote server. My main issue lies in accessing the "events" data using the function below: $scope.generate_event = function(){ ...

Accessing objects from HTML elements in jQuery can be done using the `$

Is there a way to retrieve object options and functions from an HTML element? Here is the code snippet: $.fn.test = function(options){ var me = this; this.create = function(){ me.settings = $.extend({ foo: 'foo' }, options) ...

Angular: Design dependent on attributes

Can I customize the styling of a div in accordance with a boolean property called "isActive" on my controller using Angular? <div class="col-md-3" (click)="isActive = !isActive"> <div class="center"> <i class="fa fa-calendar"& ...

Navigating with Angular: Transmitting dynamic URL parameters to components

I currently have the following routes defined: const routes: Routes = [ { path: ':product/new', children: [{ path: 'std/:country', component: SignUpComponent, data: { ...

Tips for adjusting the time format within Ionic 3 using TypeScript

I currently have a time displayed as 15:12:00 (HH:MM:SS) format. I am looking to convert this into the (3.12 PM) format. <p class="headings" display-format="HH:mm" > <b>Time :</b> {{this.starttime}} </p> In my TypeScript code t ...

Delegating in Typescript/Angular2 without using an arrow function

Why is the result of TypeScript delegates not equal? someProperty: any; someActionsWithItems(item: any) { this.someProperty = "test"; } // When using 'this', it works fine: this.array.forEach(item => this.someActionsWithItems(item)); / ...

What method can be used to modify the src attribute of an <img> tag given that the id of the <img> element is known?

My challenge involves displaying an array of images using a *ngFor loop. itemimg.component.html <div *ngFor="let itemimg of itemimgs" [class.selected]="itemimg === selectedItemimg" (click)="onSelect(itemimg)"> <img id="{{itemim ...