Moshe L
Moshe L

Reputation: 1905

VanillaJS Promise implementation to replace jQuery notify/progress event

I am currently using a $.Deferred() object for posting messages.

When the server returns an error, I am using notify, and try again until it succeeds or fails X times.

I want to remove the usage of jQuery, ant move to ES6 related options.

I know how to use Promise(), but it can either be rejected or resolved, but not both.

Is there a better idea?

The current code:

var def = $.Deferred(),
  data = {
    iTries: 0,
    ....
  };

$.ajax({
  type: "POST",
  url: "POST",
  data: data,
  timeout: 15000,
  success: d => def.resolve(d);,
  error: function(vSocket, status, error) {
    def.notify('error'); // this I want to replace
    data.iTries++;

    //try again
    if (data.iTries < 40) window.setTimeout(function() {
      DoAgain(data, def).then(q => def.resolve(q));
    }, 9000);
    else {
      def.reject();
    }
  }
  else {
    if (data.iTries < 20) window.setTimeout(function() {
      Poll.AjaxPoll(data, def).then(q => def.resolve(q));
    }, 30000);
  }
}
});

Upvotes: 0

Views: 370

Answers (2)

Mohamed Ibrahim Elsayed
Mohamed Ibrahim Elsayed

Reputation: 2964

It seems from your code that you are using ES6 (you are using arrow functions). You can follow a recursive approach with es6 arrow functions and promises, it could be something like this:

doAjaxRequest(data) {
    fireAPICall(data).then(response => { // fireAPICall is a method that fires the post request and returns the response received from server
        if(response.status == 200) { // success (this if statement is optional)
            // handle success
            let d = response.data;
        }
    }).catch(error => { // failure
        data.iTries++;
        if(data.iTries < 40) {
            window.setTimeout(() => {doAjaxRequest(data)}, 9000); // try again
        } else if(data.iTries < 20) {
            window.setTimeout(() => {doAjaxRequest(data)}, 30000); // try again
        }
    });
}

For the fireAPICall method, you can use any promise-based http client like axios or Fetch API, here I'm using axios:

fireAPICall(data) {
    return axios.post("url", data);
}

Update: if you want handle both reject/failure event and notify/progress events, this will need some server-side collaboration. You could make your server return a 202 status code (or any other more appropriate status code) in case of progress event (not ready yet) and you can handle that in your .then() callback:

doAjaxRequest(data) {
    fireAPICall(data).then(response => { // fireAPICall is a method that fires the post request and returns the response received from server
        if(response.status === 202) { // notify/progress event
            data.iTries++;
            if(data.iTries < 40) {
                window.setTimeout(() => {doAjaxRequest(data)}, 9000); // try again
            } else if(data.iTries < 20) {
                window.setTimeout(() => {doAjaxRequest(data)}, 30000); // try again
            } else {
                return Promise.reject('poll eneded with no success'); // exceeded maximum number of times for polling, so reject. This will invoke the catch callback method with this error
            }
        } else { // handle success
            let d = response.data;
        }
    }).catch(error => { // reject/failure for some other reason
        // handle error
    });
}

Upvotes: 1

T.J. Crowder
T.J. Crowder

Reputation: 1074445

As you say, a promise can only be settled once. (This is also true of jQuery's $.Deferred, which is [now] mostly promise-like.)

Recurring notifications aren't a use case for promises. Instead, you want some kind of event emitter or publish/subscribe system (there are same extremely small ones for jQuery, such as this one [not an endorsement]), or even just a callback you call with progress updates.

Upvotes: 0

Related Questions