Очередь асинхронных действий в рефлюксе

При использовании хранилищ RefluxJS с асинхронными действиями вы можете легко получить условия гонки между вашими действиями.

Абстрактное описание проблемы

Например, наше хранилище находится в состоянии X. Асинхронное действие A вызывается из X, и до его завершения вызывается другое асинхронное действие B, также из X. Отсюда, независимо от того, какое действие заканчивается первым, оно идет не так.

  1. B заканчивается первым с состоянием Y1, A заканчивается последним и перезаписывает состояние Y1 с помощью Y2.
  2. A заканчивается первым с состоянием Y2, B перезаписывает Y2 с Y1.

Желаемое поведение будет иметь:

  A    B
X -> Y -> Z

Где B основан не на X, а на Y, и приводит к согласованному состоянию Z, вместо двух действий, основанных на одном и том же состоянии, приводящих к несогласованному состоянию:

  A   
X -> Y1   .--> Y2
  \      /  
   '----'
     B

Реализован пример проблемы

Я написал минимальный рабочий пример, работающий с Node, о проблеме, о которой я говорю.

var Q = require('q');
var Reflux = require('reflux');
var RefluxPromise = require('reflux-promise');
Reflux.use(RefluxPromise(Q.Promise));

var AsyncActions = Reflux.createActions({
    'add': { asyncResult: true }
});

var AsyncStore = Reflux.createStore({
    init: function () {
        // The state
        this.counter = 0;

        AsyncActions.add.listenAndPromise(this.onAdd, this);
    },

    // Increment counter after a delay
    onAdd: function(n, delay) {
        var that = this;
        return apiAdd(this.counter, n, delay)
        .then(function (newCounter) {
            that.counter = newCounter;
            that.trigger(that.counter);
        });
    }
});

// Simulate an API call, that makes the add computation. The delay
// parameter is used for testing.
// @return {Promise<Number>}
function apiAdd(counter, n, delay) {
    var result = Q.defer();

    setTimeout(function () {
        result.resolve(counter + n);
    }, delay);

    return result.promise;
}

// Log the store triggers
AsyncStore.listen(console.log.bind(undefined, 'Triggered'));

// Add 3 after 1 seconds.
AsyncActions.add(3, 1000);
// Add 100 almost immediately
AsyncActions.add(100, 1);

// Console output:
// > Triggered 100
// > Triggered 3

// Desired output (queued actions):
// > Triggered 3
// > Triggered 103

С этими зависимостями в package.json

{
  "dependencies": {
    "q": "^1.3.0",
    "reflux": "^0.3",
    "reflux-promise": "^1"
  }
}

Суть вопроса

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

Является ли решение очереди асинхронных действий (при условии, что это возможно в приложении Reflux) решением? Или мы должны как-то избегать этих сценариев?

1 ответ

Ваш пример кажется скорее проблемой с понятием "источник правды", чем что-либо еще. Вы сохраняете текущее состояние номера ТОЛЬКО на стороне клиента, но ТОЛЬКО обновляете его после получения подтверждения со стороны сервера о выполняемой над ним операции.

Конечно, это создаст проблемы. Вы смешиваете действия над числом и сохранением числа странным образом, где нет единого источника правды о том, что это за число в данный момент. Он находится в подвешенном состоянии между временем, когда действие называется законченным... и это не хорошо.

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

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

Затем, даже если возникают расы, у вас все еще есть источник правды для того, что это за число, и этот источник можно проверить и подтвердить. Например, если на стороне сервера хранится источник правды для числа, то API также может возвращать отметку времени для состояния этого значения каждый раз, когда оно возвращает его, и вы можете сравнить его с последним значением, которое вы получили от API для убедитесь, что вы на самом деле используете новейшее значение.

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