2016-07-22 8 views
1

Ich habe das folgende Stück Code geschrieben. makeRequest wird aufgerufen, und ich möchte dies wiederholen, wenn xhr Status 0 ist. Das Problem ist, ich bin nicht in der Lage, die richtige Versprechen zu lösen, die Wiederholungslogik ruft die richtige Antwort im n-ten Versuch, aber nicht zur Aufrufer-Methode zu propagieren.Wie wiederhole ich eine xhr-Anfrage, die ein Versprechen rekursiv für mindestens n-mal auf Status 0

Wie behebe ich dieses Problem?

var makeRequest = function(method, urlToBeCalled, payload) { 
    var deferred = $q.defer(); 
    var xhr = new XMLHttpRequest(); 
    xhr.open(method, encodeURI(urlToBeCalled), true); 
    setHttpRequestHeaders(xhr); // set headers 
    var response; 
    xhr.onload = function() { 
    if (xhr.status === 200 && xhr.readyState === 4 && xhr.getResponseHeader('content-type') !== 
     'text/html') { 
     try { 
     response = JSON.parse(xhr.response); 
     deferred.resolve(response); 
     } catch (e) { 
     deferred.reject(e); 
     } 
    } else if (xhr.status === 0) { 
     // retry here; 
     deferred.resolve(makeRequest(method, urlToBeCalled, payload)); 
    } else { 
     try { 
     response = JSON.parse(xhr.response); 
     deferred.reject(response); 
     } catch (e) { 
     deferred.reject(xhr.response); 
     } 
    } 
    }; 
    xhr.onerror = function() { 
    deferred.reject(xhr.response); 
    }; 
    xhr.send(payload); 
    return deferred.promise; 
}; 

Antwort

0

Hier ist, wie ich es Ansatz würde (siehe *** Kommentare):

var makeRequest = function(method, urlToBeCalled, payload) { 
    var deferred = $q.defer(); 
    var retries = 4;      // *** Counter 
    run();        // *** Call the worker 
    return deferred.promise; 

    // *** Move the actual work to its own function 
    function run() { 
     var xhr = new XMLHttpRequest(); 
     xhr.open(method, encodeURI(urlToBeCalled), true); 
     setHttpRequestHeaders(xhr); 
     xhr.onload = function() { 
      if (xhr.status === 200 && xhr.readyState === 4 && xhr.getResponseHeader('content-type') !== 'text/html') { 
       try { 
        response = JSON.parse(xhr.response); 
        deferred.resolve(response); 
       } catch (e) { 
        deferred.reject(e); 
       } 
      } else if (xhr.status === 0) { 
       // retry 
       if (retries--) {   // *** Recurse if we still have retries 
        run(); 
       } else { 
        // *** Out of retries 
        deferred.reject(e); 
       } 
      } else { 
       // *** See note below, probably remove this 
       try { 
        response = JSON.parse(xhr.response); 
        deferred.reject(response); 
       } catch (e) { 
        deferred.reject(xhr.response); 
       } 
      } 
     }; 
     xhr.onerror = function() { 
      deferred.reject(xhr.response); 
     }; 
     xhr.send(payload); 
    } 
}; 

Randbemerkung: Der Inhalt Ihrer ersten if Körper und die endgültige else erscheinen identisch zu sein. Ich glaube, ich Neufassung würde die gesamte onload:

xhr.onload = function() { 
    if (xhr.readyState === 4) { 
     // It's done, what happened? 
     if (xhr.status === 200) { 
      if (xhr.getResponseHeader('content-type') !== 'text/html') { 
       try { 
        response = JSON.parse(xhr.response); 
        deferred.resolve(response); 
       } catch (e) { 
        deferred.reject(e); 
       } 
      } else { 
       // Something went wrong? 
       deferred.reject(e); 
      } 
     } else if (xhr.status === 0) { 
      // retry 
      if (retries--) {   // *** Recurse if we still have retries 
       run(); 
      } else { 
       // *** Out of retries 
       deferred.reject(e); 
      } 
     } 
    } 
}; 

Re Ihr Kommentar:

Dies tut mein aktuelles Problem zu beheben, aber ist es eine Möglichkeit, alle Versprechen zu lösen, die hinzugefügt werden Callstack, wenn einer von denen gelöst ist?

Ja: Um $q dass mit kantigem ist zu tun (ich nehme an das ist, was Sie verwenden), können Sie einfach das Versprechen Sie aus dem rekursiven Aufruf zurück gehen in resolve auf latente Objekt: Da es sich um ein Versprechen, Das Zurückgestellte wird darauf warten, dass es abgerechnet wird und auf der Grundlage dessen, was dieses Versprechen verspricht, auflösen oder ablehnen. Wenn Sie dies in der Kette auf jeder Ebene zu tun, arbeiten die Auflösungen ihren Weg der Kette:

angular.module("mainModule", []).controller(
 
    "mainController", 
 
    function($scope, $q, $http) { 
 
    test(true).then(function() { 
 
     test(false); 
 
    }); 
 

 
    function test(flag) { 
 
     log(flag ? "Testing resolved" : "Testing rejected"); 
 
     return recursive(3, flag) 
 
     .then(function(arg) { 
 
      log("Resolved with", arg); 
 
     }) 
 
     .catch(function(arg) { 
 
      log("Rejected with", arg); 
 
     }); 
 
    } 
 

 
    function recursive(count, flag) { 
 
     log("recursive(" + count + ", " + flag + ") called"); 
 
     var d = $q.defer(); 
 
     setTimeout(function() { 
 
     if (count <= 0) { 
 
      // Done, settle 
 
      if (flag) { 
 
      log("Done, resolving with " + count); 
 
      d.resolve(count); 
 
      } else { 
 
      log("Done, rejecting with " + count); 
 
      d.reject(count); 
 
      } 
 
     } else { 
 
      // Not done, resolve with promise from recursive call 
 
      log("Not done yet, recursing with " + (count - 1)); 
 
      d.resolve(recursive(count - 1, flag)); 
 
     } 
 
     }, 0); 
 
     return d.promise; 
 
    } 
 
    } 
 
); 
 

 
function log() { 
 
    var p = document.createElement('pre'); 
 
    p.appendChild(
 
    document.createTextNode(
 
     Array.prototype.join.call(arguments, " ") 
 
    ) 
 
); 
 
    document.body.appendChild(p); 
 
}
pre { 
 
    margin: 0; 
 
    padding: 0; 
 
}
<div ng-app="mainModule"> 
 
    <div ng-controller="mainController"></div> 
 
</div> 
 
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

Sie können mit JavaScript eigenen Versprechungen das Gleiche tun:

test(true).then(function() { 
 
    test(false); 
 
}); 
 

 
function test(flag) { 
 
    log(flag ? "Testing resolved" : "Testing rejected"); 
 
    return recursive(3, flag) 
 
    .then(function(arg) { 
 
     log("Resolved with", arg); 
 
    }) 
 
    .catch(function(arg) { 
 
     log("Rejected with", arg); 
 
    }); 
 
} 
 

 
function recursive(count, flag) { 
 
    log("recursive(" + count + ", " + flag + ") called"); 
 
    return new Promise(function(resolve, reject) { 
 
    setTimeout(function() { 
 
     if (count <= 0) { 
 
     // Done, resolve with value 
 
     if (flag) { 
 
      log("Done, resolving with " + count); 
 
      resolve(count); 
 
     } else { 
 
      log("Done, rejecting with " + count); 
 
      reject(count); 
 
     } 
 
     } else { 
 
     // Not done, resolve with promise 
 
     // from recursive call 
 
     log("Not done yet, recursing with " + (count - 1)); 
 
     resolve(recursive(count - 1, flag)); 
 
     } 
 
    }, 0); 
 
    }); 
 
} 
 

 
function log() { 
 
    var p = document.createElement('pre'); 
 
    p.appendChild(
 
    document.createTextNode(
 
     Array.prototype.join.call(arguments, " ") 
 
    ) 
 
); 
 
    document.body.appendChild(p); 
 
}
pre { 
 
    margin: 0; 
 
    padding: 0; 
 
}

+0

Doh! Wenn oben nicht angezeigt wird, dass Sie nicht mehr versuchen, klicken Sie auf Aktualisieren. –

+0

Danke. Dies löst zwar mein aktuelles Problem, aber gibt es eine Möglichkeit, alle Versprechen zu lösen, die zum Call-Stack hinzugefügt werden, wenn einer davon gelöst wird? – arbghl

+0

@arbghl: Ja - es ist wirklich wirklich einfach, du nennst einfach "Entschlossenheit" mit einem Versprechen, und das Versprechen, auf das du es nennst, wird basierend auf diesem Versprechen auflösen oder ablehnen. Ich habe oben Beispiele hinzugefügt. –

Verwandte Themen