AngularJS中的承诺顺序

时间:2016-07-08 11:11:16

标签: javascript angularjs promise angular-promise cancellation

问题:

是否有一种“简单”的方法可以取消AngularJS中的承诺($q - / $http - )或确定承诺的解决顺序?

示例

我有一个长时间运行的计算,我通过$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时,我可以访问响应上的config-object,使用一些时间戳或其他标识符来手动排序传入的响应。然而,我希望我能以某种方式告诉角度取消过时的承诺,因此当它被解析时不会运行.then()函数。

如果没有$q的手动实现,这不起作用 - 承诺而不是$http

也许只是拒绝承诺就是要走的路?但在这两种情况下,它可能需要永远,直到最终在生成下一个请求之前解决了一个promise(在此期间会导致一个空视图)。

是否存在一些我缺少的角度API函数,或者是否存在强大的设计模式或带有promise链接的“技巧”或$ q.all来处理返回“相同”数据的多个promise?

4 个答案:

答案 0 :(得分:4)

取消承诺只是让它不会在onFulfilled阶段调用onRejectedthen函数。因此,@ user2263572提到它总是最好放弃未取消的承诺(无论如何都无法取消ES6原生承诺)并在then阶段处理这个条件(如果全局变量设置为2则忽略任务)如下面的代码段所示,我相信你可以找到很多其他方法来实现它。一个例子可能是;

很抱歉,对v resolve使用x(看起来像是核对字符),reject函数使用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));(显而易见)。

src/main/scala

答案 1 :(得分:4)

我是通过生成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. 根据网址+参数生成requestId
  2. 检入requestId

    的请求哈希表
    • 如果找不到requestId:在哈希表中生成新请求和存储承诺
    • 如果找到requestId:只需从哈希表中返回承诺
  3. 请求完成后,从哈希表中删除requestId的条目。

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

在ECMA6承诺中,there is a Promise.race(promiseArray) method。这需要一组promises作为参数,并返回一个promise。在数组中解析的第一个承诺会将其解析后的值移交给返回的promise的.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(虽然我还没有测试过它)。