Search code examples
angulartypescriptasync-awaitobservablees6-promise

Promise must be async - Angular 9


I would like to upload an array of files separately and then post other data only if the files are done. So far, each file uploads with its own request, but I can't make the other requests wait for the files to finish uploading - they all execute at the same time, despite the async/await.

Here is a simplified version of my code (where run() needs to be an observable or promise and postOtherDatas() needs to wait uploadFileList() to be done):

public async run(data: Data): Promise<Object> {
    await this.uploadFileList(data.files); // run this first
    return this.postOtherDatas(data.otherData).subscribe(); // run this only if uploadFileList() is done
}
    
public async uploadFileList(files: File[]) {
  return await Promise.all(files.map((file) => {
    this.uploadFile(file).subscribe();
  }));
}

public uploadFile(file: File): Observable<HttpEvent<any>> {
  const req = new HttpRequest('POST', `apiUrl/postfile`, 
    formData, { withCredentials: true });
  return this.http.request(req);
}
  
public postOtherDatas(formData: FormData) {
  return this.http.post('apiUrl/postotherdata',
    formData, { withCredentials: true }
  );
}

I also tried this but postOtherDatas() is never called:

public run(data: Data): Observable<Object> {
   return this.uploadFileList(data.files).then(() => this.postOtherDatas(data.otherData).subscribe())
}

Solution

  • A rxjs subscription is not a promise:

    public async uploadFileList(files: File[]) {
      return await Promise.all(files.map((file) => {
        // Not a promise!
        this.uploadFile(file).subscribe();
      }));
    }
    

    If you want to keep using promises, you can do it this way:

    public async uploadFileList(files: File[]) {
      return await Promise.all(files.map((file) => {
        return this.uploadFile(file).toPromise();
      }));
    }
    

    The same needs to be done in your run method:

    public async run(data: Data): Promise<Object> {
        await this.uploadFileList(data.files); // run this first
        return this.postOtherDatas(data.otherData).toPromise(); // run this only if uploadFileList() is done
    }
    

    However: I would propose you use rxjs more deliberately, it has some great tools for handling more complicated asynchronicity. E.g. Promise.all can be achieved using the forkJoin function.