путем создания общего промежуточного программного обеспечения на основе обещаний
Сегодня мы увидим, насколько полезным может быть универсальное промежуточное ПО, которое автоматически отправляет действия загрузка, успех и сбой в зависимости от результата оценка обещания.
Мы сосредоточимся на библиотеке управления состоянием Redux (http://redux.js.org/), но та же логика может быть применена и к другим фреймворкам.
Введение
В наши дни обещания повсюду. Они предоставляют действительно удобный способ вызова асинхронных операций, если вы хотите узнать о них больше:
Https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Promise
Если задуматься, обещание можно разделить на три состояния.
- Загрузка
- Успех
- Отказ
Эти части можно легко идентифицировать в следующем фрагменте.
console.log('loading!') doSomethingAsync() .then(() => console.log('success!')) .catch(() => console.log('failure!'))
Все мы знаем, что при разработке современного веб-приложения мы должны быть уверены, что не теряем никакой информации, чтобы обеспечить полноценное взаимодействие с пользователем. Следовательно, в архитектуре Flux состояние каждого обещания должно быть связано с правильным действием, чтобы правильно реагировать на каждое поведение. Следуя предыдущему примеру, это можно архивировать, отправив определенное действие для каждого состояния.
dispatch({ type: "FETCH_SOMETHING_LOADING" }) doSomethingAsync() .then(({ data }) => dispatch({ type: "FETCH_SOMETHING_SUCCESS", data })) .catch((err) => dispatch({ type: "FETCH_SOMETHING_FAILURE", err }))
Мы можем заметить, что независимо от того, что может случиться, мы всегда что-то отправляем, поэтому никакая информация не теряется.
Реализация
Поскольку мы определили структуру общего обещания, мы можем абстрагироваться от нашей логики и создать удобное промежуточное программное обеспечение, которое всегда будет отправлять правильное действие в нужное время, чтобы избежать написания тонны кода при каждом асинхронном вызове.
Базовое промежуточное ПО в Redux определяется как функция, которая вызывается каждый раз при отправке действия. Это базовый пример
const logger = store => next => action => {
console.log('I am a middleware!!')
}
Мы хотим запустить определенное действие, когда достигнем правильного состояния обещания. Представьте, что мы хотим получить пользователя
axios.get('https://randomuser.me/api/') // this returns a promise
Затем, чтобы заархивировать нашу цель, мы могли бы создать функцию, которая отправляет три действия:
fetchUser(dispatch){ dispatch({ type: 'FETCH_USER_LOADING' }) axios.get('https://randomuser.me/api/') .then(({ data }) => dispatch({ type: 'FETCH_USER_SUCCESS', data })) .catch(( err ) => dispatch({ type: 'FETCH_USER_FAILURE', err })) }
Он запускает действие ЗАГРУЗКА перед вызовом обещания и УСПЕХ, когда мы получаем данные, или ОТКАЗ в противном случае.
Теперь мы можем легко создать промежуточное ПО для автоматической генерации всех этих действий. Нам нужен только type
для генерации имени и сам Promise
, который можно сохранить в поле, призванное для простоты promise
.
Таким образом, все три действия можно закодировать в одно, которое будет извлекаться посредником.
// a generic async action { type:'FETCH_USER', promise: () => axios.get('https://randomuser.me/api/')}
Наконец, промежуточное ПО
const promiseMiddleware = store => next => action => { // check if it has a promise if(!action.promise || typeof action.promise.then != 'function') return next(action) next({type:`${action.type}_LOADING`}) action.promise .then(({ data }) => next({type:`${action.type}_SUCCESS`,data})) .catch(err => next({type:`${action.type}_FAILURE`,err})) } export default promiseMiddleware
Сначала мы проверяем, передаем ли мы Promise
в поле promise
. Если это так, он отправляет правильные действия.
Вот и все!
Вы можете установить промежуточное ПО с помощью npm
npm i -S redux-promise-action-middleware
Чтобы проверить это, я создал небольшой магазин:
// redux stuff import { createStore, applyMiddleware } from 'redux' import thunk from 'redux-thunk'; import logger from 'redux-logger' import promiseMiddleware from 'redux-promise-action-middleware' const initialState = { isLoading : false, user: {}, hasError: false } function userReducer(state = initialState, action) { switch(action.type){ case 'FETCH_USER_LOADING': return Object.assign({}, state, { isLoading: true }) case 'FETCH_USER_SUCCESS': const user = action.data.results[0] return Object.assign({}, state, { isLoading: false, user, hasError: false }) case 'FETCH_USER_FAILURE': return Object.assign({}, state, { isLoading: false, hasError: true }) default: return state } } const userStore = createStore(userReducer, applyMiddleware(thunk,promiseMiddleware,logger)); export default userStore
Теперь посмотрим, работает ли это
userStore.dispatch({ type: 'FETCH_USER', promise: axios.get('https://randomuser.me/api/')})
Мы видим, что мы успешно отправили действие ЗАГРУЗКА и действие УСПЕХ, но не записали их!
Код можно найти здесь:
Также код небольшого тестового приложения:
Https://github.com/FrancescoSaverioZuppichini/generic-promise-middleware-test
Заключение
Думаю, мы улучшили работу с обещаниями и Flux / Redux! Наше промежуточное ПО может автоматически запускать действие в зависимости от его типа и оценки обещания. Если у вас есть отзывы, оставьте, пожалуйста, комментарий.
Спасибо за чтение.
Франческо Саверио
P.S.
Я не являюсь поклонником React / Redux, но мне нравится, как структурировано промежуточное ПО и моя реализация потока для Vue, Flue (https://medium.com/@FrancescoZ/flue-another-flux-library-82ff43f70899) , Я использовал тот же подход, поэтому вы действительно можете использовать в Vue созданное нами промежуточное программное обеспечение.