2016-09-21 3 views
0

Ich habe eine Kette von Deferred in einem JavaScript Submit-Handler, die Reihe von AJAX-Aufrufe an eine API machen, bis das gewünschte Ergebnis zurückgegeben wird. Es hat gut funktioniert, aber ich habe ein paar Änderungen vorgenommen und jetzt kann ich nicht herausfinden, wo ich falsch gelaufen bin, denn der neue Code scheint eine gebrochene Kette von Versprechen zu haben.Deferred löst nicht auf

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 
}); 

Die Konsole Ausgang davon

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

was Sinn macht: Die erste Linie ist kurz vor waitForJobStatus genannt zu werden, dann waitForJobStatus versucht einmal und nicht einen Job zu finden, dann versucht es erneut nach 1 Sekunde und findet einen Job, so dass es die data protokolliert, und schließlich, bevor ich auflösend, füge ich eine Konsolennachricht hinzu, um zu beweisen, dass wir es in den bedingten gemacht haben.

Aber dann die console.log('got job status, updating and resolving'); nie ausgelöst - die waitForJobStatus nicht gelöst bekommen, ich denke, so dass die then in createLinkAPIJob nie ausgelöst

Antwort

1

Sie den Problembereich falsch identifiziert. In diesem if Zweig wird das Zurückgestellte fein aufgelöst. Das Problem ist die else Zweig:

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

Hier def wird löst nie (und ist entweder nicht rejcted)! Dies ergibt sich aus Ihrer Verwendung der deferred antipattern - wenn Sie nicht eine verzögerte, return ing von einem then Callback würde tatsächlich funktionieren. Du solltest nur solche Verkettungen machen. Wenn Sie Funktionen aufrufen, die bereits Versprechungen zurückgeben, erstellen Sie niemals eine Zurückgestelltes (Sie haben es ungewöhnlich gut getan, indem Sie bereits $.promiseDelay ausgeklammert haben)!

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 …; 
    }); 
} 
+0

Vielen Dank für die Antwort und Beispielcode. Diese Antwort hat mir gestern nur halb Sinn gemacht (wie die meisten Antworten, die ich über Versprechungen in den letzten Monaten erhalten habe) - was mich genug frustrierte, dass ich JavaScript mit Promises gekauft und es gestern und heute Morgen gelesen habe, was längst überfällig war. Möge ich nie wieder mit einem Missverständnis über Versprechen kommen müssen! – fildred13