AngularJS 中的承诺顺序

Order of promises in AngularJS

问题:

是否有 "easy" 方法来取消 AngularJS 中的($q-/$http-)承诺或确定解决承诺的顺序?

例子

我有一个很长的 运行ning 计算,我通过 $http 请求结果。在初始承诺得到解决之前,某些操作或事件需要我重新启动计算(并因此发送新的 $http 请求)。因此我想我不能使用像

这样的简单实现
$http.post().then(function(){
    //apply data to view
})

因为我无法确保响应按照我发送请求的顺序返回 - 毕竟我想在所有承诺都得到正确解决时显示最新计算的结果。

但是我想避免等待第一个响应,直到我发送这样的新请求:

const timeExpensiveCalculation = function(){
    return $http.post().then(function(response){
        if (isNewCalculationChained) {return timeExpensiveCalculation();}            
        else {return response.data;}
    })
}

想法:

使用 $http 时,我可以访问响应上的配置对象,以使用一些时间戳或其他标识符来手动对传入响应进行排序。但是我希望我可以告诉 angular 以某种方式取消过时的承诺,因此当它得到解决时 运行 .then() 函数不会被取消。

如果没有手动实现 $q-promises 而不是 $http,这将不起作用。

也许直接拒绝承诺才是正确的选择?但是在这两种情况下,在生成下一个请求之前最终解决承诺可能需要很长时间(同时导致空视图)。

是否有一些 angular API-我缺少的功能或是否有稳健的设计模式或 "tricks" 具有承诺链或 $q.all 来处理多个承诺return "same" 数据?

取消承诺只是让它在then阶段不调用onFulfilledonRejected函数。因此,正如@user2263572 提到的那样,最好放弃未取消的承诺(ES6 原生承诺无论如何都不能取消)并在 then 阶段处理这种情况(比如如果全局变量设置为 2,则忽略任务如以下代码片段所示),我相信您可以找到许多其他方法来做到这一点。一个例子可以是;

抱歉,我对 resolve 使用 v(看起来像检查字符),对 reject 函数使用 x(很明显)。

var    prom1 = new Promise((v,x) => setTimeout(v.bind(null,"You shall not read this"),2000)),
       prom2,
validPromise = 1;
prom1.then(val => validPromise === 1 && console.log(val));
// oh what have i done..!?! Now i have to fire a new promise
prom2 = new Promise((v,x) => setTimeout(v.bind(null,"This is what you will see"),3000));
validPromise = 2;
prom2.then(val => validPromise === 2 && console.log(val));

我通过生成一个 requestId 来做到这一点,并在 promise 的 then() 函数中检查响应是否来自最近的 requestId.

虽然这种方法实际上并没有取消之前的承诺,但它确实提供了一种快速简便的方法来确保您正在处理最新的请求响应。

类似于:

var activeRequest;
function doRequest(params){
    // requestId is the id for the request being made in this function call
    var requestId = angular.toJson(params); // I usually md5 hash this

    // activeRequest will always be the last requestId sent out
    activeRequest = requestId;

    $http.get('/api/something', {data: params})
        .then(function(res){
            if(activeRequest == requestId){
                // this is the response for last request

                // activeRequest is now handled, so clear it out
                activeRequest = undefined;
            }
            else {
                // response from previous request (typically gets ignored)
            }
        });
}

编辑: 在旁注中,我想补充一点,跟踪 requestId's 的概念也可以应用于防止重复请求。例如,在我的 Data 服务的 load(module, id) 方法中,我做了一个像这样的小过程:

  1. 根据 URL + 参数生成 requestId
  2. 签入 requestId

    的请求哈希-table
    • 如果未找到 requestId:生成新请求并在散列中存储 promise-table
    • 如果找到 requestId:只需 return 来自散列的承诺-table
  3. 请求完成后,从散列中删除 requestId 的条目-table。

我仍在尝试找出一种对此进行单元测试的好方法,但您可以尝试这种策略:

var canceller = $q.defer();
service.sendCalculationRequest = function () {
    canceller.resolve();
    return $http({
        method: 'GET',
        url: '/do-calculation',
        timeout: canceller.promise
    });
};

在 ECMA6 承诺中,there is a Promise.race(promiseArray) method。这将一系列承诺作为其参数,并且 returns 是一个单一的承诺。数组中第一个解析的承诺会将其已解析的值传递给返回的承诺的 .then,而第二个出现的其他数组承诺将不会被等待。

示例:

var httpCall1 = $http.get('/api/something', {data: params})
    .then(function(val) { 
        return { 
            id: "httpCall1"
            val: val
        }
    })
var httpCall2 = $http.get('/api/something-else', {data: params})
    .then(function(val) { 
        return { 
            id: "httpCall2"
            val: val
        }
    })
// Might want to make a reusable function out of the above two, if you use this in Production
Promise.race([httpCall1, httpCall2])
    .then(function(winningPromise) {
        console.log('And the winner is ' + winningPromise.id);
        doSomethingWith(winningPromise.val);
    });

您可以将它与 Promise polyfil 一起使用,或者 look into the q.race that someone's developed for Angular(尽管我还没有测试过)。