0

Hi I'm making a ReactJS project and I'm trying to modify a function to make it more readable... It have a lot of API calls so it have a lot of .then() .catch() blocks. Do you have some suggestion to avoid all of this .then() .catch() blocks?? Because now is really unreadable.

const saveImgAvatar = (img_input) => {
        setLoadingUploadImg(true)
        //Salvo su aws e creo il cdn
        let file = img_input
        // Split the filename to get the name and type
        let fileParts = img_input.name.split('.');
        let fileName = context.currentUser.uid + "-img-avatar";
        let fileType = fileParts[1];
        console.log("file name" + fileName)
        console.log("file parts" + fileType)
        axios.post(process.env.REACT_APP_API_URL, {
            fileName: fileName,
            fileType: fileType
        })
            .then(response => {
                var returnData = response.data.data.returnData;
                var signedRequest = returnData.signedRequest;
                var url = returnData.url;

                //Ora salvo l'url 
                console.log("Recieved a signed request " + signedRequest);

                // Put the fileType in the headers for the upload
                var options = {
                    headers: {
                        'Content-Type': fileType
                    }
                };
                axios.put(signedRequest, file, options)
                    .then(result => {
                        setTalentImgAvatar(url)
                        //setCurrentImgAvatar(returnData.url)
                        //Salvo sul db
                        axios.put(process.env.REACT_APP_API_URL, { img_url: url })
                            .then(res => {
                                setLoadingUploadImg(false)
                                toggleShowEditImg(!showEditImg)
                                setAlertProfConf(true)
                                setTimeout(() => {
                                    setAlertProfConf(false)
                                }, 1000)
                            })
                            .catch(function (error) {
                                console.log(error)
                                notifyMessage("error", "Errore nell'aggiornamento immagine")
                                setLoadingUploadImg(false)
                            })

                    })
                    .catch(error => {
                        //Apro un messaggio di errore
                        notifyMessage("error", "Errore")
                        //Notificato l'errore fermo lo spinner di loading
                        setLoadingUploadImg(false)
                    })
            })
            .catch(error => {
                console.log(JSON.stringify(error));
                //Apro un messaggio di errore
                notifyMessage("error", "Errore")
                //Notificato l'errore fermo lo spinner di loading
                setLoadingUploadImg(false)
            })

    }
Dave
  • 93
  • 1
  • 7
  • 3
    You can use async/await instead: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await – Lance Jun 09 '20 at 20:06
  • You can also return a promise from within `then` or `catch` to continue chaining without needing to nest callbacks (in circumstances where the same `catch` clause is used) – zzzzBov Jun 09 '20 at 20:11
  • Does this answer your question? [How to chain nested promises containing then and catch blocks?](https://stackoverflow.com/questions/56239772/how-to-chain-nested-promises-containing-then-and-catch-blocks) or [Removing nested promises](https://stackoverflow.com/q/21999178/215552) for a less jQuery-oriented question. – Heretic Monkey Jun 09 '20 at 20:14

3 Answers3

2

Try using asnyc/await.

const saveImgAvatar = async (img_input) => {
  setLoadingUploadImg(true)
  //Salvo su aws e creo il cdn
  let file = img_input
  // Split the filename to get the name and type
  let fileParts = img_input.name.split('.');
  let fileName = context.currentUser.uid + "-img-avatar";
  let fileType = fileParts[1];
  console.log("file name" + fileName)
  console.log("file parts" + fileType)
  const response = await axios.post(process.env.REACT_APP_API_URL, {
    fileName: fileName,
    fileType: fileType
  });
               
  var returnData = response.data.data.returnData;
  var signedRequest = returnData.signedRequest;
  var url = returnData.url;

  //Ora salvo l'url 
  console.log("Recieved a signed request " + signedRequest);

  // Put the fileType in the headers for the upload
  var options = {
    headers: {
      'Content-Type': fileType
    }
  };
  const result = await axios.put(signedRequest, file, options);
  setTalentImgAvatar(url)
  //setCurrentImgAvatar(returnData.url)
  //Salvo sul db
  const res = axios.put(process.env.REACT_APP_API_URL, { img_url: url });
  setLoadingUploadImg(false)
  toggleShowEditImg(!showEditImg)
  setAlertProfConf(true)
  setTimeout(() => {
    setAlertProfConf(false)
  }, 1000)
})
Dave Salomon
  • 3,287
  • 1
  • 17
  • 29
  • But in this case I can not handle errors correctly, or I'm wrong again? – Dave Jun 09 '20 at 20:13
  • You can use `try`/`catch` around each request, but you'll end up with a similar problem, where your code becomes more and more indented. You probably want to split it out into separate/smaller functions. – Dave Salomon Jun 09 '20 at 20:15
  • A big try / catch that cover all it can be enough? – Dave Jun 09 '20 at 20:30
  • Absolutely, you can `try`/`catch` the whole thing -- you'll just need to work out which request failed if you want to handle them differently. Maybe your API returns a different code, etc. – Dave Salomon Jun 09 '20 at 20:31
1

One major benefit of using async/await is that you avoid callback hell.

There are lots of great resources online from which to learn from, but you could use async/await to implement something like this:

try {

  const res1 =  await asyncProcess1(); //Current execution context is suspended until this process is finished
  const res2 =  await asyncProcess2(res1);
  const res3 =  await asyncProcess3(res2);


} catch (exception) {
  //do something with exception
}


 async function asyncProcess1() {
     //async task here: 

    return "done";
  }
Frosty619
  • 1,381
  • 4
  • 23
  • 33
0

Since put(), post(), and etc return a promise just return that in the first level callback and deal with the response in the a new top level then(). The resolved data from the returned promise will be passed to the next then() callback in the chain, for example:

axios.post().then((data)=>{
  /*process datat*/
  return axios.put();
}).then((postResponseData)=>{
  /*process datat*/
  return axios.put();
}).then((putResponseData)=>{
  /*process datat*/
  return axios.post();
}).catch((err)=>{
  /*...*/
});

You could also use async/await syntax to give a look of a more sync style code, but would need to manage the catch individually:

async function run(){
  let resp = await axios.post(...);
  /*... process response ...*/
  let resp2 = await axios.put();
  /*... process the next response ... */
  let resp3 = await.post(...);
  /* etc etc */
  return finalData;
}

run().then((data)=>{  console.log(data); });
Patrick Evans
  • 41,991
  • 6
  • 74
  • 87
  • But in the second solution I can not handle errors without add a plenty of try catch blocks right? – Dave Jun 09 '20 at 20:19