2016-07-22 13 views
1

我寫下了下面這段代碼。 makeRequest被調用,我想在xhr狀態爲0時重試此操作。問題是我無法解決正確的承諾,重試邏輯在第n次嘗試中獲取正確的響應,但未能傳播到調用方法。如何重試一個xhr請求,它在狀態0上遞歸地遞歸至少n次

如何解決此問題。

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

回答

0

以下是我想接近它(見***評論):

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

旁註:你最初if正文的內容和最終else似乎是相同的。我想我會重鑄整個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); 
      } 
     } 
    } 
}; 

回覆您的評論:

這確實解決我目前的問題,但沒有解決所有這些被添加到一個承諾方式調用堆棧,如果其中任何一個被解決?

是:要做到這一點有角的$q(我認爲這就是你使用的是什麼),你可以通過你從遞歸調用回resolve您的延期對象上承諾:由於這是一個承諾,延期將等待它被解決,並根據承諾做什麼來解決或拒絕。如果這樣做,在鏈各個層面,決議開闢道路,環比上漲:

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>

你可以做同樣的事情用JavaScript自己的諾言:

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!如果您沒有看到上面的「重試」,請點擊刷新。 –

+0

謝謝。這確實解決了我目前的問題,但有沒有辦法解決所有添加到調用堆棧的承諾,如果其中任何一個解決? – arbghl

+0

@arbghl:是的 - 這實際上很簡單,你只需要一個承諾來調用'resolve',並且你所稱的承諾將根據該承諾來解決或拒絕。我已經在上面添加了示例。 –