I am faced with a challenge involving an asynchronous function and the best approach to executing it synchronously

I devised the following plan:

// Primary Function to Follow
        // Capture necessary local data
        // Transform into required editable format
        // Iterate through user's local images
            // Append image names to converted data
            // Convert IMAGE to base64 and insert result into zip file
            that.uriToBase64(imageFilePath).then((result) => {
                console.log("image as base64: ", result);
                zip.file(arrayItem2.name, result, {base64: true});
            });

        // Re-transform data for saving it
        // Incorporate data into zip file
        // Generate and preserve zip file in user's local storage

        // Function uriToBase64()
    

The Issue at Hand

There seems to be a timing discrepancy with the step 'Incorporate data into zip file' occurring before images actually get added. Despite using .then within the 'Convert IMAGE to base64' step, everything inside seems to execute after overall completion. Consequently, my zip file is being saved without the essential images. Various attempts using async/await syntax have failed to rectify this problem, specifically ensuring the image data gets included inside the zip file during each iteration.

Considering the Promise returned by the uriToBase64() function, employing .then to receive the data should ideally "pause" the loop until the data is successfully added via the zip.file() command, followed by iteration to the next image. Is this not the case? And if so, what would be the correct method of waiting for this outcome, keeping in mind the current algorithm structure?


Resolution Attempts

First Attempt

To address this issue, I attempted to make numerous adjustments, yet success eluded me once more. Updated Algorithm:

// Main Function
        // Get some local data
        // Convert to some format I need and add to a global variable
        // Loop through user's local images
            // Add +1 to new global variable 'imageCounter'
            // Edit the converted data (now in global variable) to add the image's names to it
            // Convert IMAGE to base64 and add result into zip file
            that.uriToBase64(imageFilePath).then((result) => {
                console.log("image as base64: ", result);
                zip.file(arrayItem2.name, result, {base64: true});
                that.prepareForWrite();

                // Check if this is the last iteration and run function again.
                if (thisIsLastIteration == true) { that.prepareForWrite(); }                        
            });

    //prepareForWrite() function
        // imageCounter - 1
        // if imageCounter < 1
            // Convert data again to save it
            // Add data to zip file
            // Generate zip file and save in user's local storage

    //uriToBase64() function
    

In this manner, all received data appears accurate, yet "Generate zip file and save it in user’s local storage" merely includes the first image, ultimately corrupting the file. Second Attempt

Mentioned earlier, my efforts with async/await proved futile. The entire function was divided into smaller functions, with async/await attempted on specific segments needing precedence. My latest use of async/await looked akin to this:

// async Main Function
        // Get some local data
        // Convert to some format I need to edit
        // Iterate through user's local images
            // Edit the converted data to add the image's names to it
            // Convert IMAGE to base64 and add result into zip file
            let result = await that.uriToBase64(imageFilePath);
                    console.log(result);
                    zip.file(arrayItem2.name, result, {base64: true});

        // Convert data again to save it
        // Add data to zip file
        // Generate zip file and save it in user's local storage

    //uriToBase64() function
    

Third Attempt

After conducting various tests, receiving data synchronously from the uriToBase64() function propelled me onto distinct paths. Irrespective of obtaining base64 strings, upon generating the ZIP file, only one zipped and corrupted image persisted alongside the other images and primary file being neglected. This realization led me to consider the file zipping process before ZIP file creation. Consequently, I explored solutions to uncover answers. A function present in the JSZIP library seemed promising, allowing scrutiny of content added to the ZIP file. Pursuing relevant inquiries directed me towards this discovery here. The implementation undertaken:

mainFunction() {
        let string64 = 'veryLongBase64String';

        let b64s = [];
        let arrayOfPromises = [];

        b64s.push(string64);
        b64s.push(string64);
        console.log(b64s);

        b64s.forEach((b64, index) => {
            let fileName = index + ".jpg";
            arrayOfPromises.push(this.addFileToZip(fileName, b64)); 
        });

        Promise.all(arrayOfPromises)
        .then(this.zip.generateAsync({type:"blob"})
            .then((content) => {
                let filePath = this.file.externalRootDirectory + '/app_downloads/';
                this.file.writeFile(filePath, "testZip.zip", content).then(() => {
                    alert("File saved!");
                })
                .catch((err) => {
                    alert("Error while creating file: " + JSON.stringify(err));
                });
            })
        );
    }

    addFileToZip(name: string, b64: string): Promise<string> {
        this.zip.file(name, b64, {base64: true});
        return this.zip.file(name).async("uint8array");
    }
    

Answer №1

To effectively manage this situation, you can utilize the "async" and "await" keywords.

Take a look at the demonstration provided in this link.

https://stackblitz.com/edit/typescript-async-await-pipeline

To streamline your code, consider moving the specified segment from your snippet to a separate file:

// Iterate through user's local images
    // Modify the transformed data by incorporating image names
    // Convert IMAGE to base64 and append it to the zip file
    that.uriToBase64(imageFilePath).then((result) => {
        console.log("image as base64: ", result);
        zip.file(arrayItem2.name, result, {base64: true});
    });

Once all promises are resolved using "Promise.all", proceed with persisting your zip file to storage.

Answer №2

The answer to the question can be found here provided by user @username123

Essentially, when converting base64 strings and creating a ZIP file, changing the file type from blob to arraybuffer resolved any errors encountered during the process.

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

What are the steps to code this in Angular14 syntax?

Here's the code snippet: constructor(obj?: any) { this.id = obj && obj.id || null; this.title = obj && obj.title || null; this.description = obj && obj.description || null; this.thumbnailUrl = obj && obj.thumbnailUrl || null; this. ...

TypeORM: When generating a migration, a SyntaxError is thrown stating that an import statement cannot be used outside a

While configuring TypeORM in my NextJS TypeScript project, I encountered an issue where I received the error message: SyntaxError: Cannot use import statement outside a module when attempting to create migrations for my entities. ...

Tips for updating the checkbox state while iterating through the state data

In my component, I have the ability to select multiple checkboxes. When a checkbox is selected, a corresponding chip is generated to visually represent the selection. Each chip has a remove handler that should unselect the checkbox it represents. However, ...

Injecting configurable middleware dependencies in Nest.js allows for dynamic customization of middleware

While many tutorials demonstrate how to inject providers into dynamic modules, most of them only focus on services and not middleware. The challenge arises when attempting to create reusable middleware that also requires injected providers. For instance, ...

Obtaining data from a BehaviorSubject for numerous dynamically generated components

As I work on developing a dynamic preview feature, I'm experimenting with drag-and-drop functionality to generate components in a canvas. These components can be duplicated within the same canvas, and upon dropping them, settings like styling are appl ...

How can I retrieve data during a double-click event in Kendo Grid using Angular?

How can I retrieve data on the doubleClick event in a Kendo Grid? I want to access the same object that is fetched during the selected event, which would be the dataitem at the selected index row. HTML: <kendo-grid #myGrid [data]="gridDat ...

Creating an array object in TypeScript is a straightforward process

Working on an Angular 4 project, I am attempting to declare an attribute in a component class that is an object containing multiple arrays, structured like this: history: { Movies: Array<Media>, Images: Array<Media>, Music: Array<Medi ...

Tips for quietly printing a PDF document in reactjs?

const pdfURL = "anotherurl.com/document.pdf"; const handleDirectPrint = (e: React.FormEvent) => { e.preventDefault(); const newWin: Window | null = window.open(pdfURL); if (newWin) { newWin.onload = () => ...

What is the process for applying cdkDropList to the tbody when using mat-table instead of a traditional HTML table?

I have been experimenting with the mat-table component in my Angular project, following a simple example from the documentation: <table mat-table [dataSource]="dataSource" class="mat-elevation-z8"> <!--- These columns can be ...

The service method call does not occur synchronously

In my OrderServer class, I am utilizing an OrderService to connect to a database and retrieve data every minute. The communication with the web app is handled through SocketIO. Here is a snippet of the code: export class OrderServer { // some required fie ...

multi-level or interconnected sessions in apps for mobile devices

In my current setup, I am utilizing the Ionic framework for the front end with Angular.js, and on the back end, I rely on Spring Boot for data management and API handling. I have implemented a single session and CSRF token exchange to ensure security betw ...

What is the best way to interpret the property 'subscribe' of an undefined object?

After cloning a MEAN stack application from GitHub, I made minor modifications by changing all instances of the word 'employee' to 'sensor'. The build was successful without any issues. However, upon launching localhost:4200, I encounte ...

What is the best way to showcase a variable from a typescript file in an HTML file with Angular?

In my TypeScript file, I have the following function: ngOnInit() { if (sessionStorage['loyalPage']) { this.page = Number(sessionStorage['loyalPage']); } this.webService.getLoyalPlayers(this.pag ...

Angular4 provider being integrated into an AngularJS application offers the provider functionality

I have recently migrated my app from AngularJS to Angular4. Now, I want to create a provider in Angular4 and inject it into the app.config in AngularJS. Here is what I currently have: import { UtilsService } from './../utils/utils.service'; imp ...

Instructions on retrieving keyboard input values from Angular's Material Datepicker using Reactive Forms

Currently, I am using Angular along with material datepicker within Reactive Forms and moment's MomentDateModule. My concern lies in extracting the value that a user types into the form via keyboard input. If you wish to see an example of this scenar ...

Passing an array of objects as properties in React components

My functional component looks like this: function ItemList({ items }: ItemProps[]) { return <p>items[0].name</p> } Here is how I'm creating it: <ItemList items={items} /> The array items contains objects in the format [{name: &ap ...

Using TypeScript to extract types from properties with specific types

My current challenge involves working with a filter object derived from an OpenAPI spec. The structure of this object is illustrated below: export interface Filters { field1: string[] field2: string[] field3: boolean field4: number } My goal is to ...

The function Event.target.value is coming back as null

I've been working on creating a timer window, and I've set up a separate component for it. However, I'm facing an issue with passing the time from the main component to the child component. The problem lies in the fact that the state of the ...

Custom styles for PrimeNG data tables

Struggling to style the header of a datatable in my Angular project using PrimeNG components. Despite trying various solutions, I am unable to override the existing styles. Even attempting to implement the solution from this PrimeNG CSS styling question o ...

Angular 2: Changing the name of a component

Looking for guidance on renaming a component in my Angular2 application. I've already updated all the necessary files - changed the file names and folder name, as well as made adjustments to specific files such as y.component.ts, app.routing.ts, and a ...