fildred13
fildred13

Reputation: 2350

Deferred is not resolving

I have a chain of deferreds in a javascript submit handler that make series of AJAX calls to an API until the desired result is returned. It worked well, but I made a tweak and now I am unable to figure out where I went wrong, because the new code seems to have a broken chain of promises.

function createLinkAPIJob(type) {
  //code to create a request
  return $.ajax(request)
}

function getJobStatus(jobID) {
  return $.ajax({
    url: Urls['find_job'](jobID),
    contentType: 'application/JSON',
    method: 'GET'
  })
}

// utility function to create a promise that is resolved after a delay
$.promiseDelay = function(t) {
  return $.Deferred(function(def) {
    setTimeout(def.resolve, t);
   }).promise();
 }

function waitForJobStatus(jobID, timeStarted) {
  return $.Deferred(function(def) {

    getJobStatus(jobID).then(function(data) {
      console.log(data);
      var isFinished = data['job']['finished'];
      var jobStatus = 'incomplete';
      var jobStatus = data['job']['job_status'];
      if (isFinished === true) {
        /***** HERE IS THE PROBLEM AREA *****/
        console.log('resolving wait for job status');
        def.resolve(jobStatus);
        //also tried: return jobStatus;
      } else {
        return $.promiseDelay(1000).then(function() {
          return waitForJobStatus(jobID, timeStarted);
        });
      }
    });

  }).promise();
}

function executeLinkAPIJob(type) {
  return $.Deferred(function(def) {

    createLinkAPIJob(type).then(function(response) {
      var jobID = response['data']['job_id'];
      var timeStarted = new Date().getTime() / 1000;
      console.log('waiting for job to finish');
      waitForJobStatus(jobID, timeStarted).then(function(jobStatus) {
        console.log('got job status, updating and resolving');
        // A bunch of code here that doesn't matter for this issue...
        def.resolve();
      });
    });

  }).promise();
} 

// I know this seems stupid in this example, but jobs is sometimes a longer array
jobs = [executeLinkAPIJob(type=type)]
.when.apply($, jobs).then(function() {
  // do something
});

The console output of which is

waiting for job to finish
Object {job: "{"error": "Job not found"}"}
Object {job: Object}
resolving wait for job status

Which makes sense: the first line is just before waitForJobStatus getting called, then waitForJobStatus tries once and fails to find a job, then tries again after 1 second and finds a job, so it logs the data, and finally just before I resolve, I add a console message to prove that we made it into the conditional.

But then the console.log('got job status, updating and resolving'); never fires - the waitForJobStatus doesn't get resolved, I guess, so the then in createLinkAPIJob never fires

Upvotes: 0

Views: 193

Answers (1)

Bergi
Bergi

Reputation: 665536

You misidentified the problem area. In that if branch, the deferred is resolved fine. The problem is the else branch:

… else {
  return $.promiseDelay(1000).then(function() {
    return waitForJobStatus(jobID, timeStarted);
  });
}

Here, def is never resolves (and is not rejcted either)! This stems from your usage of the deferred antipattern - if you had not used a deferred, returning from a then callback would indeed work. You are supposed to do such chaining only. If you are calling functions that already return promises, never create a deferred (you did unusually well by factoring out $.promiseDelay already)!

function waitForJobStatus(jobID, timeStarted) {
  return getJobStatus(jobID).then(function(data) {
    console.log(data);
    var isFinished = data['job']['finished'];
    var jobStatus = 'incomplete';
    var jobStatus = data['job']['job_status'];
    if (isFinished === true) {
      console.log('resolving wait for job status');
      return jobStatus; // this is correct indeed
    } else {
      return $.promiseDelay(1000).then(function() {
        return waitForJobStatus(jobID, timeStarted);
      });
    }
  });
}

function executeLinkAPIJob(type) {
  return createLinkAPIJob(type).then(function(response) {
    var jobID = response['data']['job_id'];
    var timeStarted = new Date().getTime() / 1000;
    console.log('waiting for job to finish');
    return waitForJobStatus(jobID, timeStarted);
  }).then(function(jobStatus) {
    console.log('got job status, updating and resolving');
    // A bunch of code here that doesn't matter for this issue...
    return …;
  });
}

Upvotes: 1

Related Questions