-1

What i want to do is to upload file on server, then get URL of uploaded file and preview it. Files can be more than one. For that purpose i have written following code:

 let filesURL=[];
 let promises=[];
 if(this.state.files_to_upload.length>0) {

    for(let i=0; i<this.state.files_to_upload.length; i++) {
        promises.push(this.uploadFilesOnServer(this.state.files_to_upload[i]))
    }

    Promise.all(promises).then(function(result){
        console.log(result);
        result.map((file)=>{
            filesURL.push(file);
        });
    });
    console.log(filesURL);
}
const uploadedFilesURL=filesURL;
console.log(uploadedFilesURL);

console.log(filesURL); give me the values returned by Promise.all.
And i want to use these values only when Promise.all completes properly. But, i am facing problem that lines console.log(uploadedFilesURL); excutes first irrespective of Promise.all and give me undefined values.I think i am not using promises correctly, can anyone please help me?
uploadFileOnServer code is:

uploadFilesOnServer(file)
  {
    let files=[];
    let file_id='';

    const image=file;
    getImageUrl().then((response) => {
      const data = new FormData();
      data.append('file-0', image);
      const {upload_url}  = JSON.parse(response);
      console.log(upload_url);

      updateProfileImage(upload_url, data).then ((response2) => {
      const data2 = JSON.parse(response2);
      file_id=data2;
      console.log(file_id);
      files.push(file_id);
      console.log(files);
        });
    });
    return files;
  }
vassiliskrikonis
  • 566
  • 2
  • 10
Muhammad Hamid Raza
  • 185
  • 2
  • 3
  • 13
  • You need to learn more about Promises in Javascript. First of all, `console.log(uploadedFilesURL);` _will_ be executed immediatelly. The code you need to run _after_ a promise is resolved goes into the `.then()` callback. Secondly, `Promise.all()` takes as argument an array of promises, whilst you pass it an array of whatever `file_id` type is. – vassiliskrikonis Aug 16 '17 at 10:01
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Thomas Aug 16 '17 at 10:02

3 Answers3

4

You have to do this on the .then part of your Promise.all()

Promise.all(promises)
.then(function(result){
   console.log(result);
   result.map((file)=>{
     filesURL.push(file);
   });
   return true; // return from here to go to the next promise down
 })
 .then(() => {
     console.log(filesURL);
     const uploadedFilesURL=filesURL;
     console.log(uploadedFilesURL);
 })
Prasanna
  • 4,125
  • 18
  • 41
4

No, promise is asynchronous and as such, doesn't work the way you think. If you want to execute something after a promise completed, you must put it into the promise's then callback. Here is the example based on your code:

uploadFilesOnServer(file) {
  let files=[];
  let file_id='';

  const promise = getImageUrl()
  .then((imageUrlResponse) => {
    const data = new FormData();

    data.append('file-0', file);

    const { upload_url }  = JSON.parse(imageUrlResponse);

    console.log(upload_url);

    return updateProfileImage(upload_url, data);
  })
  .then ((updateImageResponse) => {
    file_id= JSON.parse(updateImageResponse);

    console.log(file_id);

    files.push(file_id);

    console.log(files);

    return files;
  });

  return promise;
}

let filesPromise = Promise.resolve([]);

if(this.state.files_to_upload.length > 0) {
  const promises = this.state.files_to_upload.map((file) => {
    return this.uploadFilesOnServer(file);
  });

  filesPromise = Promise.all(promises).then((results) => {
    console.log(results);

    return [].concat(...results);
  });
}

// This is the final console.log of you (console.log(uploadedFilesURL);)
filesPromise.then((filesUrl) => console.log(filesUrl));

A good book to read about ES6 in general and Promises in particular is this book Understanding ECMAScript 6 - Nicholas C. Zakas

Edit:

Here is an simple explanation of the example code:

  1. The uploadFilesOnServer is a function that takes a file, upload it and will return the file URL when the upload completes in the future in the form of a promise. The promise will call its then callback when it gets the url.

  2. By using the map function, we create a list of url promises, the results we've got from executing uploadFilesOnServer on each file in the list.

  3. The Promise.all method waits for all the promises in the list to be completed, joins the list of url results and create a promise with the result which is the list of urls. We need this because there is no guarantee that all of the promises will complete at once, and we need to gather all the results in one callback for convenience.

  4. We get the urls from the then callback.

Tr1et
  • 873
  • 11
  • 25
  • Thanks alot @Tr1et. Your given snippet of code solves the issue. What i understand in above is that first we map "files_to_upload" array to "file" and pass it to "uploadFileOnServer". This function returns a promise which have "files" array. Then you call promise.all on every promise and concatenate all data in one array. Now "filePromise" will have all the combined data and it is accessible in whole ".then()" of filePromise. Please tell me if i get it correctly or correct me where i am getting it wrong. Thanks.! – Muhammad Hamid Raza Aug 16 '17 at 11:29
  • I've added an explanation in the answer because it's kind of long. Is there anything you don't understand? – Tr1et Aug 16 '17 at 12:08
  • Now, what i want is to get to know which files were unable to upload on server. That is which promises were failed. How can i do that? – Muhammad Hamid Raza Aug 26 '17 at 07:23
0

This is the way async code works. You cannot expect your console.log(filesURL); to work correctly if it is being called syncronously after async call to fetch files from server.

Regarding to your code there are several problems:

1.uploadFilesOnServer must return Promise as it is async. Therefore:

  uploadFilesOnServer(file)
  {
    let files=[];
    let file_id='';

    const image=file;
    return getImageUrl().then((response) => {
      const data = new FormData();
      data.append('file-0', image);
      const {upload_url}  = JSON.parse(response);
      console.log(upload_url);

      updateProfileImage(upload_url, data).then ((response2) => {
      const data2 = JSON.parse(response2);
      file_id=data2;
      console.log(file_id);
      files.push(file_id);
      console.log(files);
      return files;
        });
    });
  }

2.Inside your main function body you can assess results of the Promise.all execution only in its respective then handler.

As a side note I would recomment you to use es7 async/await features with some transpilers like babel/typescript. This will greatly reduce the nesting/complications of writing such async code.

Amid
  • 21,508
  • 5
  • 57
  • 54