Цепочка доставки реактивов Flux

Я пытаюсь использовать React with Flux architecture и наткнулся на одно ограничение, с которым я не могу справиться. Проблема заключается в следующем:

  • Там есть магазин, который прослушивает событие. Событие имеет идентификатор объекта. Нам нужно получить объект, если необходимо, и сделать его выбранным.
  • Если у хранилища нет объекта с этим id - он запрашивает. В обратном вызове мы отправляем другое событие для хранения, которое отвечает за выбор.
  • Если у хранилища есть объект - я бы хотел отправить событие выбора, но я не могу, потому что отправка выполняется.

Самое лучшее решение, с которым я столкнулся, - это обернуть внутреннюю отправку в setTimeout(f, 0), но выглядит страшно.

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

Есть ли у кого-нибудь хорошие подходы к решению таких проблем?

var selectItem(item) {
    AppDispatcher.dispatch({
        actionType: AppConstants.ITEM_SELECT,
        item: item
    });
}

// Item must be requested and selected.
// If it in store - select it.
// Otherwise fetch and then select it.
SomeStore.dispatchToken = AppDispatcher.register((action) => {
    switch(action.actionType) {
        case AppConstants.ITEM_REQUESTED:
            var item = SomeStore.getItem(action.itemId);
            if (item) {
                // Won't work because can't dispatch in the middle of dispatch
                selectItem(item);
            } else {
                // Will work
                $.getJSON(`some/${action.itemId}`, (item) => selectItem(item));
            }
    }
};

Ответ 1

Вы пишете своего диспетчера? setTimeout(f, 0) - прекрасный трюк. Я делаю то же самое в своем минимальном потоке здесь. Там ничего страшного. Javascript concurrency модель довольно проста.

Более надежные реализации диспетчера потоков должны обрабатывать это для вас.

Ответ 2

Если ITEM_SELECT - это событие, которое будет обрабатывать другой Store:

Вы ищете dispatcher.waitFor(array<string> ids): void, который позволяет использовать SomeStore.dispatchToken, который register() возвращает, чтобы обеспечить соблюдение порядка, в котором хранятся магазины обрабатывать событие.

В хранилище, скажем, мы называем его OtherStore, который обрабатывал событие ITEM_SELECT, должен обрабатывать событие ITEM_REQUEST, но сначала называть dispatcher.waitFor( [ SomeStore.dispatchToken ] ), а затем получать любой результат из SomeStore через открытый метод, например SomeStore.getItem().

Но из вашего примера кажется, что SomeStore ничего не делает для своего внутреннего состояния с ITEM_REQUEST, поэтому вам просто нужно переместить следующие строки в OtherStore несколькими незначительными изменения

// OtherStore.js
case AppConstants.ITEM_REQUESTED:
        dispatcher.waitFor( [ SomeStore.dispatchToken ] );// and don't even do this if SomeStore isn't doing anything with ITEM_REQUEST
        var item = SomeStore.getItem(action.itemId);
        if (item) {
            // Don't dispatch an event, let other stores handle this event, if necessary
            OtherStore.doSomethingWith(item);
        } else {
            // Will work
            $.getJSON(`some/${action.itemId}`, (item) => OtherStore.doSomethingWith(item));
        }

И снова, если другой магазин должен обрабатывать результат OtherStore.doSomethingWith(item), они также могут обрабатывать ITEM_REQUESTED, но называть dispatcher.waitFor( [ OtherStore.dispatchToken ] ), прежде чем продолжить.

Ответ 3

Итак, глядя на свой код, вы устанавливаете свойство "selected" на элементе, чтобы оно было проверено/выбрано в вашем пользовательском интерфейсе/компоненте? Если да, просто сделайте ту часть функции, в которой вы уже находитесь.

if(item) {
    item.selected = true;
    //we're done now, no need to create another Action at this point, 
    //we have changed the state of our data, now alert the components 
    //via emitChange()

    emitChange();
}

Если вы хотите отслеживать текущий выбранный элемент в Магазине, просто введите идентификатор и/или объект как приватный var и установите его аналогично.

var Store = (function(){

    var _currentItem = {};
    var _currentItemID = 1;

    function selectItem(item) {

          _currentItem = item;
          _currentItemID = item.id;
          emitChange();
    }


    (function() {
         Dispatcher.register(function(action){
               case AppConstants.ITEM_REQUESTED:
                   var item = SomeStore.getItem(action.itemId);
                   if (item) {
                        selectItem(item);
                   } else {
                   $.getJSON(`some/${action.itemId}`, (item) => 
                        selectItem(item);   
                   }
         });
    })();


    return {
         getCurrentlySelectedItem: function() {
              return _currentItem;
         },
         getCurrentlySelectedItemID: function() {
              return _currentItemID;
         }
    }

})();

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

Теперь, если у вас проблемы с перекрестным хранилищем, а другой магазин заботится о некоторых конкретных изменениях некоторых данных в вашем первоначальном хранилище, здесь будет задействована функция waitFor (ids). Она эффективно блокирует выполнение до первого магазина обновляется, а затем другой может продолжить выполнение, заверил, что другие данные Хранилища находятся в допустимом состоянии.

Я надеюсь, что это имеет смысл и решает вашу проблему, если нет, сообщите мне, и, надеюсь, я смогу нуля в лучшем случае.