Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript Promise .then() and .catch firing at the same time

I am turning my comments on wordpress into an ajax driven system.

All is good so far, until I bumped into an issue with the .catch() method firing straight after the .then() method.

Here is my code...

Ajax engine

commentAPI: function(action, encoded, userID) {
    let self = this;

    return new Promise(function (resolve, reject) {
       //console.log("ajax call to " + self.ajaxURL + " with action " + action);

        jQuery.ajax({               
            url: self.ajaxURL,
            type: 'post',
            data: 'action='+action+'&data='+encoded,
            dataType: 'json',
            success: function(data, code, jqXHR) { resolve(data); },
            fail: function(jqXHR, status, err) { console.log('ajax error = ' + err ); reject(err); },
            beforeSend: function() {} //display loading gif
        });
    });
}, 

The method handling the comment form submission

handleReplyFormSubmit: function(form) {
    let self = this;

    this.removeErrorHtml(form);

    // Serialize form to name=value string
    const formdata = jQuery(form).serialize();

    // Validate inputs
    // * Wordpress doing this for now and providing error resonse 

    // Encoode data for easy sending
    const encodedJSON = btoa( formdata );

    this.commentAPI('dt_submitAjaxComment', encodedJSON).then(function(response){
        console.log('firing then');

        if( response.error == true ) {
            self.printFormError(form, response.errorMsg);
        }

        else { 
            let html = response.commentHTML;
            console.log('html returned' + html)
            jQuery(form).append(html);
            Jquery(form).remove();
        }

    }).catch(function(err) {            
        console.log('firing catch');

        if( err !== undefined && err.length > 0 ) { 
            self.printFormError(form, err);
        }

        else { 
            self.printFormError(form, 'Unkown error');
        }
    });

    return false;
},

The code is doing what it is supposed to, but the catch method is being fired too, which is making error handling frustrating...

Output of code. Notice the console.log('firing catch') is being fired

Notice how this is being fired

console.log('firing catch')

But this isn't ( within the ajax fail function )

console.log('ajax error = ' + err );

Am I doing something wrong?

like image 680
Dale Woods Avatar asked Aug 04 '18 13:08

Dale Woods


1 Answers

Promises

It is common that a then fires, and then a later catch: it means that some error was encountered in your then handler code, so the catch fires. Catch handlers will fire:

  • If there is an error in the asynchronous operation, so the Promise is rejected.
  • If there is an error in any previous then handler.

So, the following code:

Promise.resolve()
.then( () => {
  console.log('this will be called');
  throw new Error('bum');
  console.log('this wont be logged');
})
.catch(err => {
  console.log('this will be logged too');
  console.log(err); // bum related error
});

Will yield logs both from then and catch handlers.

Your code

Inside your then handler there is this code:

    else { 
        let html = response.commentHTML;
        console.log('html returned' + html)
        jQuery(form).append(html);
        Jquery(form).remove();
    }

Notice how the last line have Jquery instead of jQuery, which is a typo. I bet this is the error causing the catch to fire.

On top of that

Modern verions of jQuery just return a promise from $.ajax(), so there is no need to wrap it into another promise.

This code:

commentAPI: function(action, encoded, userID) {
  let self = this;

  return new Promise(function (resolve, reject) {
  //console.log("ajax call to " + self.ajaxURL + " with action " + action);

    jQuery.ajax({               
        url: self.ajaxURL,
        type: 'post',
        data: 'action='+action+'&data='+encoded,
        dataType: 'json',
        success: function(data, code, jqXHR) { resolve(data); },
        fail: function(jqXHR, status, err) { console.log('ajax error = ' + err ); reject(err); },
        beforeSend: function() {} //display loading gif
    });
  });
},

Should just be:

commentAPI: function(action, encoded, userID) {
    return jQuery.ajax({
        url: this.ajaxURL,
        type: 'post',
        data: 'action='+action+'&data='+encoded,
        dataType: 'json',
        beforeSend: function() {} //display loading gif
    });
},

So you can handle success and failure in commentApi then and catch handlers, instead of passing success and fail callbacks to resolve or reject a wrapping Promise.

The ajax success params

The success callback param takes three parameters. However, Promises usually just take one.

But, jQuery does pass the same three arguments to the then handler, so in case you need access to them, you can still use them in the handler:

this.commentAPI('dt_submitAjaxComment', encodedJSON).then(function(data, code, jqXhr){
 // the three arguments will be accessible here.
...
}
like image 58
Sergeon Avatar answered Oct 21 '22 06:10

Sergeon