Angular - Async function does not resolve a rejected promise

Currently, my component utilizes an async method for handling file uploads. Here is an example:

//component
uploadPhotos = async (event: Event) => {
    const upload = await this.uploadService.uploadPhotos(event, this.files, this.urls);
}

The UploadService returns a promise containing the updated files and path files once the upload is completed. Everything works as intended when the promise successfully reaches resolve(). However, if reject() is triggered, the code continues execution until it encounters resolve() inside reader.onload().

// service
uploadPhotos(event: Event, oldFiles: File[], oldUrls: string[]): Promise<{files: File[], urls: string[]}> {
     return new Promise((resolve, reject) => {
          const files = (event.target as HTMLInputElement).files;

          if ((files.length + oldFiles.length) > 5) {
               this.alertService.error('Número máximo de fotos permitidos é 5.');
               reject();
          }
      
          for (let i = 0; i < files.length; i++) {
              const exists = oldFiles.findIndex(file => file.name === files[i].name);
              if (exists === -1) {
                   if (files[i].type === 'image/png' || files[i].type === 'image/jpeg') {
                      oldFiles.push(files[i]);

                      const reader = new FileReader();
                      reader.onerror = (error: any) => {
                          this.alertService.error(`Erro ao carregar a imagem: ${error}`);
                          reject();
                      };
                      reader.readAsDataURL(files[i]);
                      reader.onload = () => {
                          oldUrls.push(reader.result);
                          if (i === files.length - 1) { resolve({ files: oldFiles, urls: oldUrls }); }
                 };
            } else {
                this.alertService.error('Formato inválido. Somente imagens do formato Png, Jpeg e Jpg são permitidos.');
                reject();
            }
       }
  }
});
}

Is there a way to skip the reader.onload() block if reject() is called before resolve()?

Answer №1

Indeed, reject() and resolve() are simple (callback) function calls. They do not impact the control flow of your function. To control the flow, use return or if/else.

Additionally, within your new Promise, there is a loop. If an error occurs in one file reader, it will not affect the others. It is advised to handle promises on a more specific level. Here is a suggestion:

readFile(file: File): Promise<string> {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onerror = (error: any) => reject(error);
        reader.onload = () => resolve(reader.result);
        reader.readAsDataURL(file);
    });
}
// service
async uploadPhotos(event: Event, oldFiles: File[], oldUrls: string[]): Promise<{files: File[], urls: string[]}> {
    try {
        const files = (event.target as HTMLInputElement).files;
        if ((files.length + oldFiles.length) > 5) {
            throw new Error('Maximum number of allowed photos is 5.');
        }
        const newFiles = Array.from(files).filter(newFile =>
            !oldFiles.some(oldFile => oldFile.name === newFile.name)
        );
        if (!newFiles.every(file => file.type === 'image/png' || file.type === 'image/jpeg' || file.type === 'image/jpg')) {
            throw new Error('Invalid format. Only Png, Jpeg, and Jpg image formats are allowed.');
        }
        const newUrls = await Promise.all(newFiles.map(file =>
            this.readFile(file).catch(error => {
                throw new Error(`Error loading image: ${error}`);
            })
        ));
        return {
            files: oldFiles.concat(newFiles),
            urls: oldUrls.concat(newUrls)
        };
    } catch(err) {
        this.alertService.error(err.message);
        throw new Error("Something went wrong during file upload");
    }
}

Answer №2

To ensure that the load listener does not trigger, you can include the load listener and then remove it in the error handler:

reader.onerror = (error: any) => {
  this.alertService.error(`An error occurred while loading the image: ${error}`);
  reader.removeEventListener('load', loadHandler);
  reject();
};
reader.readAsDataURL(files[i]);

const loadHandler = () => {
  oldUrls.push(reader.result);
  if (i === files.length - 1) {
    resolve({ files: oldFiles, urls: oldUrls });
  }
};
reader.addEventListener('load', loadHandler);

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 file size exceeds the server's upload limit, despite making changes to the php.ini file

I've encountered a problem trying to upload an .OBJ file to the server, resulting in an 'Error 1' message. The uploaded file surpasses the upload_max_filesize directive specified in php.ini This error is detailed on this page - http://ph ...

Having difficulties creating a new instance of a class

I'm encountering an issue with this TypeScript code import Conf = require('conf'); const config = new Conf(); The Problem: the expression is not constructable and the imported module lacks construct signatures It's puzzling because th ...

angular form validation methods

I am having issues with validating the form below. The required validation message is not appearing for my input type="file" field, even though the validation works well for my other textboxes. How can I validate the input as a required field? Please see ...

How to retrieve an element by its class using JavaScript and Jquery while implementing a

I am working on creating a quiz example using Jquery, but I am facing some challenges when trying to manipulate CSS classes with hover in Jquery. .right{ background-color: white; } .wrong { background-color: white; } .right:hover { background-color ...

What is the best way to store the dom in cache to ensure that the page remains unchanged when navigating back using the back button?

When adding models to my JavaScript application's model collection using AJAX calls, I encounter an issue where if I click on a model and go to the next page, all the loaded models disappear when I hit the back button. What is the most effective way t ...

NPM is complaining about the absence of a start script

Apologies for any language barriers in my English. I'm currently facing an issue while trying to deploy an app on Heroku using the heroku local web command in the terminal. The error message ERR! missing script: start keeps popping up, even though the ...

The chosen selection automatically deactivates the checkbox with a matching value

I am looking for a solution where selecting an option will automatically disable the checkbox with the corresponding value. The existing methods I have come across are static and rely on hardcoded values. <select name="pickOne" id="pickOn ...

Issues with Angular 6 HTTPInterceptor interface in production environments

Currently, I am utilizing the HttpInterceptor interface to include an authorization header in HTTP requests. @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor( private localStorage: LocalStorageService, ...

Guide to implementing dynamic custom validations in Angular 2 using ReactiveForms

I have a specific requirement where I need to create logic that dynamically assigns object array data to an input control. Additionally, I want to update my array based on user input and trigger validation to ensure there are no duplicate entries. I am uti ...

Step-by-step guide on importing CSS into TypeScript

I have a global CSS file where I've defined all the colors. I attempted to import that value into TypeScript but it didn't work. This is my latest attempt: get sideWindowStyle(): any { switch (this.windowStyle) { case 'classicStyl ...

Tips for assigning unique non-changing keys to siblings in React components

I've been searching for a solution for more than an hour without success. The structure of the data is as follows: const arr = [ { id: 1, title: 'something', tags: ['first', 'second', 'third'] }, { id: 2, t ...

Utilizing index signatures in TypeScript mapped types

Is there a way to abstract over the type { 'k': number, [s: string]: any } by creating a type alias T such that T<'k', number> results in the desired type? Take a look at the following example: function f(x: { 'k': numb ...

After 15 minutes of inactivity in the Angular project, the JWT (Json Web Token) token expires leading to the need for renewal or refresh

I have integrated a JWT token authentication service in my Angular project with an ASP.Net Core API as the backend. Here's how I set it up: Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddAuthentication( ...

Struggling to make the upvoting feature function properly in the Thinkster MEAN Stack Tutorial

While following the MEAN Stack tutorial on this website, I decided to modify my code to utilize Controller as instead of using $scope as demonstrated in their examples. I am encountering an issue with the upvote functionality. Whenever I click to upvote a ...

Transmit the standard information via an AJAX POST inquiry

I need to send a default data with each ajax post request, but the current code is sending the data for all requests. Can you provide some guidance on how to fix this issue? $.ajaxSetup({ data: { token: $('#token').attr(&a ...

Limiting the length of parameters in an Angular directive

Is there a character limit for the parameter being sent to this directive? I'm encountering an issue with my code: header = JSON.stringify(header); columnObj = JSON.stringify(columnObj); $compile('<div column-filter-sort header=' + heade ...

Bypass VueJs Typescript errors within the template section with Typescript

My VueJs App is functioning properly, but there is one thing that bothers me - a TypeScript error in my template block. Is there a way to handle this similar to how I would in my script block? <script setup lang="ts"> //@ignore-ts this li ...

Ionic 2's Navigation Feature Failing to Function

I need to implement a "forgot password" feature on my login page. When a user clicks the button, they should be redirected to the "forgot password" page. Below is the code snippet from my login.html <button ion-button block color="blue" (cli ...

Surprising literal found at the second position

Encountered an error while trying to display a date as an array on an HTML page. The current implementation is causing an issue in the p-calendar tag within ngmodel, where date2[i] is being displayed based on the index i derived from p-datalist. I am retur ...

Safari is causing issues with HTML5 Video playback

I have a client with a media-heavy website containing numerous video and audio files. While the videos load perfectly on Chrome, Firefox, and IE, they do not load on Safari for Windows. Here's a snippet of the code: <video controls="controls" type ...