Триггерный запрос, затем действие, затем другой запрос и действие - PullRequest
0 голосов
/ 28 марта 2019

Только начал изучать RxJS и ему это нравится, но есть одна проблема, которую я не могу решить.

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

Я узнал, как получить и сохранить результат анализа ключевых слов и как отправить поисковый запрос, но не могу найти, как это объединить. Главная проблема для меня - как мне проверить, есть ли ключевые слова в магазине chach, а затем просто пропустить первую часть и отправить поисковый запрос.

const initialState = {
    keywords: {}, // keywords cache
    query: [], // current query
    items: [], // search result
    error: null,
};

const reducers = {
    setQuery: (state, { payload }) => {
        state.query = payload;
    },

    setItems: (state, { payload }) => {
        state.items = payload;
    },

    setError: (state, { payload }) => {
        state.error = payload;
    },

    addKeyword: (state, { payload }) => {
        state.keywords[payload.keyword] = payload;
    },
};

const search = createSlice({
    initialState,
    reducers,
});

const { actions } = search;

const setQueryEpic = (action$, state$) => action$.pipe(
    ofType(actions.setQuery.type),
    // This part is skipped if all keywords already in state
    flatMap(({ payload }) => payload),
    filter(keyword => !state$.value.keywords[keyword]),
    mergeMap(
        keyword => ajax
            .getJSON(`/analyze/keyword?keyword=${keyword}`),
    ),
    map(keywordData => actions.addKeyword(keywordData)),
    // This part does request and set search result. Should wait for keywords analyze
    switchMap((action) => {
        const state = state$.value
        const params = {
            keywords_vectors: state.query.map(queryKeywords => state.keywords[queryKeywords].vector),
        };
        return ajax
            .post(`/search`, JSON.stringify(params))
            .pipe(
                map(data => data.response.items),
            );
    }),
    map(items => actions.setItems(items)),
    catchError(error => of(actions.setError(error.message))),
);

ОБНОВЛЕНИЕ: Нашли это решение

const setQueryEpic = (action$, state$) => action$.pipe(
    ofType(actions.setQuery.type),
    flatMap(({ payload }) => {
        const requests = payload.map((keyword) => {
            if (state$.value.keywords[keyword]) {
                // keyword in cache
                return of(state$.value.keywords[keyword]);
            }
            // analyze new keyword
            return ajax
                .getJSON(`/analyze/keyword?keyword=${keyword}`)
        });
        // wait for all requests
        return forkJoin(requests);
    }),
    switchMap((keywords) => {
        const params = {
            keywords_vectors: keywords.map(data => data.vector),
        };
        // create actions to add keywords to cache
        const addKeywordActions = keywords
            .filter(data => !state$.value.keywords[data.keyword])
            .map(data => actions.addKeyword(data));

        return ajax
            .post(`/search`, JSON.stringify(params))
            .pipe(
                map(data => data.response.items),
                // combine all actions
                map(items => [actions.setItems(items), ...addKeywordActions]),
            );
    }),
    flatMap(action => action),
    catchError(error => of(actions.setError(error.message))),
);

Но мне не нравится получать addKeywordActions из внешней области видимости, и если вы отправляете несколько поисковых запросов switchMap сбрасывает всю эту цепочку. Было бы хорошо сохранить данные ключевых слов

...