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

Listener of events calculates the outcome

In need of help with retrieving the current coordinates of a clicked point on Google Maps. Here is my code snippet: let latLng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude); getCoords() { google.maps.event.addListener ...

Navigating to a precise element within a page in Angular with flawless redirection

I recently encountered an issue where I had to add a span element with a specific ID in my HTML code to ensure that clicking on the Reply button would navigate to it. However, this modification was only necessary for the last element on the page. While the ...

Creating an Injectable AppSettings Class in Angular 2

Currently, I am working with Angular 2 within an ASP.NET Core application, although the specifics of that setup are not crucial to my current issue. My main task at hand involves fetching configuration values from the server and integrating them into my An ...

Assign a variable to set the property of a class

Could something similar to this scenario be achievable? const dynamicPropName = "x"; class A { static propName = 1 // equivalent to static x = 1 } A[dynamicPropName] // will result in 1 or would it need to be accessed as (typeof A)[dynamicPropN ...

Update the styling of buttons in CSS to feature a unique frame color other

Can anyone help me with styling Bootstrap buttons and removing the blue frame around them after they're clicked? Here's what I've tried: https://i.stack.imgur.com/jUD7J.png I've looked at various solutions online suggesting to use "ou ...

Issue with onDblClick event in Angular5 and PrimeNG's p-listbox

I encountered an issue while using p-listbox's onDblClick event as it does not return the selected list element. Instead, the event object only contains the value of 'this'. {"originalEvent":{"isTrusted":true}} HTML Blockquote <!-- S ...

Encountered an issue with Angular while trying to import scss variables: Module parse failed due to an unexpected token at

Our project previously utilized a palette for importing styles, which functioned correctly in Angular 13. However, upon upgrading to Angular 14, the palette no longer works as expected. Below are the specific details of the issue: Error: Module parse faile ...

Error encountered when trying to use the .find function in Typescript: "The expression is not callable."

Environment Details: typescript 4.5.5 Error Summary An issue occurred stating: "This expression is not callable. Each member of the union type '{ <S extends User>(predicate: (this: void, value: User, index: number, obj: User[]) => value ...

Choose the object's property name using TypeScript through an interface

Consider a simplified code snippet like the following: interface MyBase { name: string; } interface MyInterface<T extends MyBase> { base: MyBase; age: number; property: "name" // should be: "string" but only p ...

You are unable to link to <custom directive selector> because it is not recognized as a valid property of 'div'

I am currently working on a project in StackBlitz, and you can find the link here: https://stackblitz.com/edit/angular-fxfo3f?file=src/directives/smooth-height.directive.ts I encountered an issue: Error in src/components/parent/parent.component.html (2:6) ...

What is the most secure method to define options and retrieve their values in a type-safe manner?

I am currently utilizing a library that offers an interface with a great deal of flexibility. type Option = number | { x?: number; y?: number; z?: number; } interface Options { a?: Option; b?: Option; c?: Option; d?: Option; } function init ...

Utilizing LocalStorage with Angular 6 BehaviorSubject

I'm struggling with retaining data after refreshing a page. My approach involves using a shared service to transfer data between unrelated components. Despite extensive research on LocalStorage implementation and usage, I have not been able to find a ...

The count of bits is not producing the anticipated result

Attempting to tackle the challenge of Counting Bits using JavaScript, which involves determining the number of set bits for all numbers from 0 to N, storing them in an array, and returning the result Let me provide an explanation Input: n = 5 ...

Angular 7: module not found error - unable to locate 'underscore' package

Currently tackling a project using Angular 7. Attempted to add npm install --save @types/underscore but encountered the following errors: npm WARN @agm/[email protected] requires a peer of @angular/common@^5.0.0 || ^6.0.0 but none is installe ...

Display identical text using JavaScript filter

My search filter highlight is currently displaying [object Object] instead of <mark>match values</mark> when replacing the values. This is the code I am using: this.countries.response.filter((val) => { const position = val.value.toLowerCa ...

Updating from webpack v1 to v2 using webpack-cli results in a tsx error during migration

Encountering an error during the build process after migration, I'm unsure if it's related to the recognition of tsx files or something within them that is causing issues: Failed to compile. Error in ./src/index_app.tsx Module parse fail ...

Steps for modifying the look of a button to display an arrow upon being clicked with CSS

Looking to enhance the visual appearance of a button by having an arrow emerge from it upon clicking, all done through CSS. Currently developing a React application utilizing TypeScript. Upon clicking the next button, the arrow should transition from the ...

Guide on incorporating jQuery into a jQuery plugin within an Angular 2+ application using Webpack or Angular CLI

I'm running into an issue importing a jQuery plugin into a single Angular component. It works fine in most browsers, but IE 11 is giving me this error: SCRIPT1002: Syntax error main.bundle.js (1376,1) When I investigate the error, it points me to th ...

Is it possible to create a combined header/declaration file in Golang within a single file?

My goal is to automatically generate Golang declaration files based on .json data. While with TypeScript I can consolidate types/declarations in one file using namespaces, it seems more complex to achieve the same with Golang packages and namespacing. In ...

Enhance the functionality of Immutable.js field by integrating a custom interface in Typescript

Imagine a scenario where the property name is field, essentially an immutable object. This means that methods like field.get('') and other immutable operations are available for use. Nevertheless, I have my own interface for this field which may ...