путем создания общего промежуточного программного обеспечения на основе обещаний

Сегодня мы увидим, насколько полезным может быть универсальное промежуточное ПО, которое автоматически отправляет действия загрузка, успех и сбой в зависимости от результата оценка обещания.

Мы сосредоточимся на библиотеке управления состоянием 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 созданное нами промежуточное программное обеспечение.