Рубрики
Без рубрики

Async-операции с использованием Redux-Saga

Andrés Mijares Async Операции с использованием Redux-Sagaupdate August / 2017: Я опубликовал вторую часть этой статьи, Redux-Saga Common PaptersUpdate April / 2017: Спасибо Eduardo для перевода этой статьи на португальцев, найдите ее здесь. Также этот неизвестный парень для перевода этой статьи на китайский найдет здесь. Несколько дней назад

Автор оригинала: FreeCodeCamp Community Member.

Андрес Миярес

Обновление августа/2017:

Обновление апреля/2017:

Несколько дней назад мой коллега рассказал о управлении асинхронизами. Он использовал несколько инструментов для расширения возможностей Redux. Слушая его действительно ездил домой реалии JavaScript усталость Отказ

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

Я провел последние два года, работая над угловыми проектами и наслаждаясь современным состоянием контроллера модели. И я должен сказать, что – даже если кривая обучения была проблемой, исходящей из фона Backbone.js – Угловая угловая угла действительно окупилась. Я получил лучшую работу, и у меня также имел возможность сотрудничать на интересных проектах. Я многому научился от поддерживающего сообщества углов.

Это были действительно классные дни, но, ну, Усталость должна идти на (TradeMark ожидается), и я двигаюсь с модой: реагировать, redux и сагами.

Несколько лет назад я столкнулся со статьей под названием Сравнивание цепей обещания По Томас Берлесон . Я многому научился от чтения. Даже два года спустя я все еще вспоминаю много этих пониманий.

В эти дни я был мигрировать, чтобы реагировать И я нашел много власти в Redux и используя SAGAS для управления операциями Async. Поэтому я пишу это, чтобы заимствовать от Thomas ‘Post и создать аналогичный подход, использующий Redux-Saga Отказ Вот надеясь, что это возвращает одолжение Вселенной и помогает некоторым людям понять, как эти важные технологии работают.

Отказ от ответственности: Я буду работать с тем же сценарием и продлен, я надеюсь (если мне повезет), чтобы создать обсуждение обоих подходов. Я предполагаю, что читатель имеет некоторое основное понимание обещаний, реагировать, redux и (d’Oh!) … JavaScript.

Первые вещи в первую очередь.

По словам Yassine elouafi Создатель Redux-Saga:

В основном вспомогательная библиотека, которая позволяет нам организовать все асинхронные и распределенные операции на основе генераторов функций SAGAS и ES6. Если вы хотите узнать больше о самой узоре SAGA, КАМИ MCCAFLEY сделал отличную работу в этом видео И более о генераторах функций. Проверьте этот бесплатный EGGHEAD видео (по крайней мере, это было бесплатно, когда я разместил эту статью).

Полет деловой панели

Томас установил случай, когда мы собираемся воссоздать. Окончательный код – здесь И демонстрация – здесь Отказ

Сценарий идет так:

Как мы видим, последовательность трех API Call: GetDeparture -> GetFlight -> GetForeCast, поэтому наш класс Service API выглядит так:

class TravelServiceApi {
 static getUser() {   return new Promise((resolve) => {     setTimeout(() => {       resolve({            email : "somemockemail@email.com",            repository: "http://github.com/username"       });     }, 3000);   }); }
 static getDeparture(user) {  return new Promise((resolve) => {   setTimeout(() => {    resolve({      userID : user.email,      flightID : "AR1973",      date : "10/27/2016 16:00PM"     });    }, 2500);   }); }
 static getForecast(date) {  return new Promise((resolve) => {      setTimeout(() => {        resolve({            date: date,            forecast: "rain"        });      }, 2000);   });  }
}

Это прямая API с некоторой издеванной информацией, которая позволит нам установить сценарий. Сначала нам нужно иметь пользователя. Затем с этой информацией мы получим вылет, рейс и прогноз, чтобы мы могли создать несколько некрасивых панелей приборных панелей, которые выглядят так:

Компоненты реагирования могут быть найдены здесь Отказ Они три разных компонента с представлением в магазине Redux, заданные тремя редукторами, которые выглядят так:

const dashboard = (state = {}, action) => { switch(action.type) {  case 'FETCH_DASHBOARD_SUCCESS':  return Object.assign({}, state, action.payload);  default :  return state; }};

Мы используем другой редуктор для каждой панели, с тремя разными сценариями, которые дают компонент доступа к кусочке пользователя, используя Статуэтомопс Функция redux:

const mapStateToProps =(state) => ({ user : state.user, dashboard : state.dashboard});

После того, как все установлено (да Я знаю, что я не объяснил много вещей, но я хочу сосредоточиться только на сагах …) Мы готовы играть!

Покажи мне саги

Уильям Деминг сказал один раз:

Хорошо, давайте создадим шаг за шагом процесс работы с Redux Saga.

1. Зарегистрировать саги

Я буду использовать свое собственное слово для описания, какой метод выставлен API. Если вам нужна более технические детали, не стесняйтесь ссылаться на документацию здесь .

Сначала нам нужно создать наш генератор SAGA и зарегистрируйте их:

function* rootSaga() {  yield[    fork(loadUser),    takeLatest('LOAD_DASHBOARD', loadDashboardSequenced)  ];}

Redux Saga выставляют несколько методов под названием Эффекты , Мы собираемся определить несколько из них:

  • Вилка Выполняет безблокирующую работу по прошедшему функции.
  • Возьми паузы до получения действия.
  • Гонка Запускает эффекты одновременно, затем отменяет их все, как только одна отделка.
  • Позвоните Запускает функцию. Если это возвращает обещание, приостанавливает сагу, пока обещание не будет решено.
  • Поставить отправляет действие.
  • Выберите Запускает функцию селектора для получения данных из штата
  • Takelatest Значит, мы собираемся выполнить операции, а затем вернуть только результаты последнего звонка. Если мы вызваем несколько случаев, это будет игнорировать все из них, кроме последнего.
  • возиться Возвращает результаты для всех вызовов.

Мы только что зарегистрировали два разных сага. Мы собираемся определить их позже. До сих пор мы берем один для пользователя, используя вилка и еще один Takelatest, Что собирается дождаться действия под названием « Load_Dashboard » для выполнения. Больше информации на шаге 3.

2. Введите промежуточное программное обеспечение Saga в магазин Redux.

Когда мы определяем магазин Redux и инициализируйте его, в большинстве случаев он будет выглядеть так:

const sagaMiddleware = createSagaMiddleware();const store = createStore(rootReducer, [], compose(      applyMiddleware(sagaMiddleware)  );sagaMiddleware.run(rootSaga); /* inject our sagas into the middleware*/

3. Создайте саги.

Во-первых, мы собираемся определить последовательность LoadUser Сага:

function* loadUser() {  try {   //1st step    const user = yield call(getUser);   //2nd step    yield put({type: 'FETCH_USER_SUCCESS', payload: user});
  } catch(error) {    yield put({type: 'FETCH_FAILED', error});  }}

Мы можем прочитать это так:

  • Во-первых, позвоните в функцию под названием GetUser и назначить результат в Const Пользователь Отказ
  • Позже отправьте действие под названием Fetch_user_success и пройти ценность Пользователь быть употребленным в магазине.
  • Если что-то идет плохо, отправьте действие под названием Fetch_faieded.

Как видите, действительно круто, что мы можем добавить результат операции доходности в переменной.

Теперь мы собираемся создать секвенированную сагу:

function* loadDashboardSequenced() {
 try {    yield take('FETCH_USER_SUCCESS');
  const user = yield select(state => state.user);    const departure = yield call(loadDeparture, user);
  const flight = yield call(loadFlight, departure.flightID);
  const forecast = yield call(loadForecast, departure.date);
  yield put({type: 'FETCH_DASHBOARD_SUCCESS', payload: {forecast,  flight, departure} });
  } catch(error) {    yield put({type: 'FETCH_FAILED', error: error.message});  }
}

Мы можем прочитать сагу следующим образом:

  • Ждать Fetch_user_success Действие для отправки. Это в основном будет в удержании до тех пор, пока событие не запускает его. Мы используем Возьми эффект для этого.
  • Мы принимаем значение из магазина. Выберите Эффект получает функцию, которая имеет доступ к магазину. Мы назначаем пользовательскую информацию постоянному пользователю.
  • Мы выполняем async-операцию, чтобы загрузить информацию о выезде и передавать пользователя в качестве параметра, используя Позвоните Эффект.
  • После LoadDeparture закончен, мы выполняем Loadflight с объектом отправления, выявленного в предыдущей операции.
  • То же самое будет применяться с прогнозом, нам нужно подождать, пока полет не загружен, чтобы выполнить следующий Позвоните эффект.
  • Наконец, после завершения все операции мы используем поставить Эффект на рассылку и действие в магазин и отправлять все аргументы, используя информацию, загруженную в течение всего саги.

Как видите, сага – это сборник шагов, которые ждут предыдущих действий, чтобы изменить их поведение. После завершения все информация готова к употреблению в магазине.

Довольно аккуратно, а?

Теперь давайте проверим другой случай. Рассмотрим GetFlight и GetForeCast может быть вызван одновременно. Им не нужно для того, чтобы закончить, чтобы начать другие, поэтому мы можем создать другую панель для этого случая.

Не блокировка сага

Чтобы выполнить две неблокирующие операции, нам нужно немного модифицировать нашу предыдущую сагу:

function* loadDashboardNonSequenced() {  try {    //Wait for the user to be loaded    yield take('FETCH_USER_SUCCESS');
    //Take the user info from the store    const user = yield select(getUserFromState);
    //Get Departure information    const departure = yield call(loadDeparture, user);
    //Here is when the magic happens    const [flight, forecast] = yield [call(loadFlight, departure.flightID), call(loadForecast, departure.date)];
    //Tell the store we are ready to be displayed    yield put({type: 'FETCH_DASHBOARD2_SUCCESS', payload: {departure, flight, forecast}});
} catch(error) {    yield put({type: 'FETCH_FAILED', error: error.message});  }}

Мы должны зарегистрировать доходность как массив:

const [flight, forecast] = yield [call(loadFlight, departure.flightID), call(loadForecast, departure.date)];

Таким образом, обе операции вызываются параллельно, но в конце дня мы можем дождаться обоих, чтобы обновить UI, если необходимо.

Тогда нам нужно зарегистрировать сагу в рооцу:

function* rootSaga() {  yield[    fork(loadUser),    takeLatest('LOAD_DASHBOARD', loadDashboardSequenced),    takeLatest('LOAD_DASHBOARD2' loadDashboardNonSequenced)
  ];}

Что, если нам нужно обновить пользовательский интерфейс, как только операция закончена?

Не волнуйтесь – у меня есть твоя спина.

Не секвенированные и не блокирующие саги

Мы также можем изолировать наши саги и объединить их, то есть они могут работать самостоятельно. Это именно то, что нам нужно. Давайте взглянем.

Шаг № 1 : Мы изолируем прогноз и рейс Сага. Они оба зависят от ухода.

/* **************Flight Saga************** */function* isolatedFlight() {  try {    /* departure will take the value of the object passed by the put*/    const departure = yield take('FETCH_DEPARTURE3_SUCCESS');     const flight = yield call(loadFlight, departure.flightID);     yield put({type: 'FETCH_DASHBOARD3_SUCCESS', payload: {flight}});
  } catch (error) {    yield put({type: 'FETCH_FAILED', error: error.message});  }}
/* **************Forecast Saga************** */function* isolatedForecast() {    try {      /* departure will take the value of the object passed by the put*/     const departure = yield take('FETCH_DEPARTURE3_SUCCESS');
     const forecast = yield call(loadForecast, departure.date);          yield put({type: 'FETCH_DASHBOARD3_SUCCESS', payload: { forecast, }});
} catch(error) {      yield put({type: 'FETCH_FAILED', error: error.message});    }}

Обратите внимание на что-то очень важное здесь? Вот как мы архитектор наших сагасов:

  • Они оба ждут того же Действие события (Fetch_departure3_success) для начала.
  • Они получит значение, когда это событие запущено. Подробнее на этом на следующем шаге.
  • Они будут выполнять свою асинхронитуру, используя Эффект вызова И оба получит одно и то же событие после завершения. Но они оба отправляют разные данные в магазин. Благодаря мощности Redux, мы можем сделать это без какой-либо модификации нашего редуктора.

Шаг № 2 : Давайте внесем изменения в последовательность выезда и убедитесь, что она отправляет значение отправления двумя другими сагами:

function* loadDashboardNonSequencedNonBlocking() {  try {    //Wait for the action to start    yield take('FETCH_USER_SUCCESS');
    //Take the user info from the store    const user = yield select(getUserFromState);
    //Get Departure information    const departure = yield call(loadDeparture, user);
    //Update the store so the UI get updated    yield put({type: 'FETCH_DASHBOARD3_SUCCESS', payload: { departure, }});
    //trigger actions for Forecast and Flight to start...    //We can pass and object into the put statement    yield put({type: 'FETCH_DEPARTURE3_SUCCESS', departure});
  } catch(error) {    yield put({type: 'FETCH_FAILED', error: error.message});  }}

Здесь ничего другого, пока не доберемся до поставить эффект Отказ Мы можем пройти объект к действиям, и он будет уступлен в уход постоянного в отъезде и полете Сага. Мне это нравится.

Не стесняйтесь видеть Демо И обратите внимание, как третья панель загружает прогноз до полета, потому что время ожидания выше, чтобы имитировать более медленный запрос.

В приложении к производству я бы, наверное, сделаю все возможное. Я просто хотел указать, что вы можете пройти значения при использовании поставить эффект.

Как насчет тестирования?

Сага просты в тесте, но они соединены с вашими ступенями, устанавливаются в секвенированные данные из-за природы генераторов. Давайте посмотрим пример. (И не стесняйтесь проверять все тесты в Reppo в папку Sagas):

describe('Sequenced Saga', () => {  const saga = loadDashboardSequenced();  let output = null;
it('should take fetch users success', () => {      output = saga.next().value;      let expected = take('FETCH_USER_SUCCESS');      expect(output).toEqual(expected);  });
it('should select the state from store', () => {      output = saga.next().value;      let expected = select(getUserFromState);      expect(output).toEqual(expected);  });
it('should call LoadDeparture with the user obj', (done) => {    output = saga.next(user).value;    let expected = call(loadDeparture, user);    done();    expect(output).toEqual(expected);  });
it('should Load the flight with the flightId', (done) => {    let output = saga.next(departure).value;    let expected = call(loadFlight, departure.flightID);    done();    expect(output).toEqual(expected);  });
it('should load the forecast with the departure date', (done) => {      output = saga.next(flight).value;      let expected = call(loadForecast, departure.date);      done();      expect(output).toEqual(expected);    });
it('should put Fetch dashboard success', (done) => {       output = saga.next(forecast, departure, flight ).value;       let expected = put({type: 'FETCH_DASHBOARD_SUCCESS', payload: {forecast, flight, departure}});       const finished = saga.next().done;       done();       expect(finished).toEqual(true);       expect(output).toEqual(expected);    });});
  1. Убедитесь, что вы импортируете все эффекты и функции помощников, которые вы собираетесь проверить.
  2. Когда вы храните значение на выходе, вам необходимо пройти данные издевания к следующей функции. Обратите внимание на третий, четвертый и пятый тест.
  3. За сценой каждый генератор перемещается к следующей строке после выхода, когда называется следующий метод. Вот почему мы используем Saga.next (). Значение здесь.
  4. Эта последовательность устанавливается в камне. Если вы измените шаги на SAGA, тест не пройдет.

Заключение.

Мне очень нравится проверить новые технологии и в разработке передней части, мы нашли новые вещи почти ежедневно. Это как мода: когда-то что-то принято сообществом, это как все хотят его использовать. Иногда я нахожу много ценностей в этих вещах, но все еще важно сесть и проверять, действительно ли нам что-то нужно.

Я нашел Thunks легче реализовывать и поддерживать. Но для более сложной операции Redux-Saga делает действительно отличную работу.

Еще раз, я благодарю Томасу за вдохновение для этого поста. Я надеюсь, что кто-то найдет столько вдохновения в этом посте, как я делал в своем:).

Если у вас есть какие-либо вопросы, не стесняйтесь Чирикать у меня . Я рад помочь.

Если вам больше интересует эту тему, не стесняйтесь проверить часть 2 этого серии Распространенные узоры Redux-Saga.

Медицинский инженер Для большего количества контента, пожалуйста, рассмотрите подписаться на мой канал www.youtube.com.

Наконец, не стесняйтесь проверить свои проекты с открытым исходным кодом в данный момент:

Оригинал: “https://www.freecodecamp.org/news/async-operations-using-redux-saga-2ba02ae077b3/”