Заказ обещаний в AngularJS

Вопрос:

Есть ли "простой" способ отменить ($q- /$http-) обещания в AngularJS или определить порядок выполнения обещаний?

пример

У меня есть долгосрочные расчеты, и я запрашиваю результат через $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-объекту в ответе, чтобы использовать некоторые временные метки или другие идентификаторы, чтобы вручную упорядочить входящие ответы. Однако я надеялся, что смогу просто сказать angular как-то отменить устаревшее обещание и, таким образом, не запускать функцию.then(), когда она будет решена.

Это не работает без ручной реализации для $q-обещает вместо $http хоть.

Может быть, просто отказаться от обещания - это путь? Но в обоих случаях может потребоваться вечность, пока, наконец, не будет выполнено обещание, прежде чем будет сгенерирован следующий запрос (что в свою очередь приводит к пустому представлению).

Есть ли какая-то угловая API-функция, которую мне не хватает, или есть надежные шаблоны проектирования или "хитрости" с цепочкой обещаний или $q.all для обработки нескольких обещаний, которые возвращают "одни и те же" данные?

4 ответа

Решение

Я делаю это, генерируя requestIdи в обещании 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 на основе URL + параметры.
  2. проверить в запросах хэш-таблицу для requestId

    • если requestId не найдено: сгенерировать новый запрос и сохранить обещание в хэш-таблице
    • если requestId найдено: просто верните обещание из хеш-таблицы
  3. Когда запрос завершится, удалите requestIdзапись из хеш-таблицы.

Отмена обещания просто заставляет его не ссылаться на onFulfilled а также onRejected функции на then этап. Так как @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));

Я все еще пытаюсь найти хороший способ для юнит-тестирования, но вы можете попробовать такую ​​стратегию:

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

В обещаниях ECMA6 есть Promise.race(promiseArray) метод. Это принимает массив обещаний в качестве аргумента и возвращает одно обещание. Первое обещание разрешить в массиве передаст его разрешенное значение .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 или заглянуть в q.race, который кто-то разработал для Angular (хотя я его не тестировал).

Другие вопросы по тегам