$q.all 在收到 404 响应时中止所有承诺

$q.all aborts all promises when gets a 404 response

我有一组承诺,我想同时运行。我正在使用 $q.all 来实现这一点。当一个承诺 returns 一个 404 响应时,任何其他承诺都未实现。我希望能够在 $q.all 函数上单独获得错误承诺。我该怎么做?

var idPais = 13, idUF = 20;
var promises = [
    PaisAPI.list(), 
    UnidadeFederativaAPI.list(idPais), 
    MunicipioAPI.list(idUF)
];

$q.all(promises).then(
    function(values) {
        var paises = values[0].data;
        var ufs = values[1].data;
        var municipios = values[2].data;

        console.log('paises ', paises.length);
        console.log('ufs ', ufs.length);
        console.log('municipios ', municipios.length);

        $scope.paises = paises;
        $scope.unidadesFederativas = ufs;
        $scope.municipios = municipios;
    },
    function(error) {
        if (error.status == 500) {
            alert('Server error');
        }
        //when one of the promises get a 404 response any other one are unfulfilled
    }
);

我想这是根据 Promises/A+ 规范的默认行为,所有这些东西都建立在规范之上。

Documention 对于 JS 原生承诺状态:

The Promise.all() method returns a single Promise that resolves when all of the promises in the iterable argument have resolved or when the iterable argument contains no promises. It rejects with the reason of the first promise that rejects.

来自 angular docs 对于 all():

Returns a single promise that will be resolved with an array/hash of values, each value corresponding to the promise at the same index/key in the promises array/hash. If any of the promises is resolved with a rejection, this resulting promise will be rejected with the same rejection value.

跨不同框架的所有实现都应该遵循并实际遵循这种方法。

作为解决方法,如果您的代码 returns 404 错误发生在 ajax 调用之后,请尝试解决它而不是拒绝。我的意思是将这个错误视为一个积极的结果,并用一些特殊的值解决它,比如 false{error: 404, message: 'Not Found'}0 - 以便稍后显示客户端代码的差异。解决而不是拒绝应该让 all() 等到所有的承诺都完成。

您需要确保不应使 $q.all() 失败的错误都由单独的 promise 处理。你应该确保处理所有你想让 $q.all 正常完成的错误情况,但是如果有其他可能的错误仍然应该中止 $q.all 那么那些应该通过使用$q.reject。例如 return 错误 404 的长度为 -1:

function handle404(error) {
    if (error.status == 404) {
        return { data: { length: -1 }};
    }
    return $q.reject(error)
}
var promises = [
    PaisAPI.list().catch(handle404), 
    UnidadeFederativaAPI.list(idPais).catch(handle404), 
    MunicipioAPI.list(idUF).catch(handle404)
];

然后剩下的代码和以前一样。或者在这些情况下只是 return 一个空数组:

function handle404(error) {
    if (error.status == 404) {
        return { data: []};
    }
    return $q.reject(error)
}

但这与返回一个没有错误的空数组没有区别。

重构代码以在执行 $q.all 之前将值放在 $scope 上:

var idPais = 13, idUF = 20;
var promiseObj = {};

promiseObj.paises = PaisAPI.list()
  .then(function (response) {
    var paises = response.data;
    console.log('paises ', paises.length);
    $scope.paises = paises;
    return paises;
}).catch(angular.identity);

promiseObj.ufs = UnidadeFederativaAPI.list(idPais)
  .then(function (response) { 
    var ufs = response.data;
    console.log('ufs ', ufs.length);
    $scope.unidadesFederativas = ufs;
    return ufs;
}).catch(angular.identity);

promiseObj.municipos = MunicipioAPI.list(idUF)
  .then(function (response) {
    var municipios = response.data;
    console.log('municipios ', municipios.length);
    $scope.municipios = municipios;
    return municipios;
}).catch(angular.identity);

.catch(angular.identity);将被拒绝的承诺转换为已履行的承诺。每个 API 调用并行执行。

使用 $q.all 等待所有承诺完成或被拒绝:

$q.all(promiseObj).then(function (dataObj) {
     console.log("All API calls complete");
     if (dataObj.paises.status) {
         console.log("paises failed with ",dataObj.paises.status);
     };
     if (dataObj.ufs.status) {
         console.log("ufs failed with ",dataObj.ufs.status);
     };
     if (dataObj.municipos.status) {
         console.log("municipos failed with ",dataObj.municipos.status);
     };
}); 

$q.all 与其他 promise 库的区别之一是 AngularJS $q.all 接受 promise 的对象散列。

有关详细信息,请参阅 AngularJS $q Service API Reference - $q.all