Kurz über Redux-Saga-Kanäle

Guten Tag, liebe Freunde.


In diesem Artikel möchte ich den Mechanismus der Redux-Saga-Kanäle so einfach und prägnant wie möglich beschreiben, und zwar anhand von Beispielen, die nahe an echten Fällen liegen.


Also lass uns anfangen.


Problem Uhr und Gabel


Nehmen wir an, wir haben ein gewöhnliches Watch-and-Fork-Modell der folgenden Form:


import { take, fork } from 'redux-saga/effects'
function* watchRequest() {
  while (true) {
    const {payload} = yield take('INIT_REQUEST');
    // заметим, что вызов не блокирующий
    yield fork(makeRequest, payload);
  }
}
function* makeRequest(payload) { 
    // код саги
}

Dieser Ansatz ist schlecht, da, wenn mehrere INIT_REQUESTaufeinanderfolgende Ereignisse festgehalten werden, jeweils mehrere Vorstellungen gestartet werden makeRequest. Was wiederum ihr "Rennen" verursachen kann.


Und hier kommt der Kanalmechanismus zu unserer Rettung.


Die Kanäle verfügen über Puffer, die dabei helfen, kommende Ereignisse in die Warteschlange zu stellen (zum Beispiel INIT_REQUEST) und ihre sequentielle Ausführung zu organisieren (beispielsweise wird sie makeRequestmehrmals sequentiell ausgeführt).


Grob gesagt bilden die Kanäle eine FIFO-Warteschlange für die sequentielle Ausführung.


Sie werden nach Ereignisquelle klassifiziert:


  • channel- Ereignisse werden manuell in die Warteschlange gestellt put;
  • actionChannel - Ereignisse werden in der Nähe des Redux-Stores erfasst.
  • eventChannel - externe Quelle von Ereignissen, meistens Web Socket;

Lassen Sie uns jedes kurz analysieren.


Mehr über Channel


Solche Kanäle lösen normalerweise das Problem der Kommunikation zwischen Sagen. Wird sehr selten benutzt. Zum Beispiel, wenn Sie mehrere Anforderungen abgleichen müssen, die gleichzeitig gestartet werden.


channel([buffer])

Es hat ein einziges Argument buffer- den Akkumulationspuffer (wir werden die Puffer unten analysieren).


Lesen Sie mehr über actionChannel


Wird am häufigsten verwendet, wenn auf Ereignisse aus dem Redux-Store reagiert werden muss.


actionChannel(pattern, [buffer])

Nimmt zwei Argumente an:


  • pattern- Muster der erforderlichen Ereignisse sowie take;
  • buffer - Akkumulationspuffer;

Kurzes Anwendungsbeispiel:


import { take, actionChannel, call } from 'redux-saga/effects'
function* watchRequest() {
  const requestChannel = yield actionChannel('INIT_REQUEST')
  while (true) {
    const {payload} = yield take(requestChannel);
    // заметим что вызов теперь блокирующий
    yield call(makeRequest, payload);
  }
}
function* makeRequest(payload) {
    // код саги
}

Mehr zu eventChannel


In der Regel lösen Sie durch ihn das Problem der Kommunikation durch den Web-Socket.


eventChannel(subscribe, [buffer], [matcher])

Nimmt drei Argumente an:


  • subscribe- Funktion, die externe Ereignisquelle initialisiert (im folgenden Beispiel setTimeout). Die Argumente callback, genannt Emitter, werden aufgerufen, wenn Sie Daten von der Quelle an den Kanal senden müssen. Return muss abbestellen.
  • buffer - Akkumulationspuffer;
  • matcher - Funktion zum Filtern eingehender Nachrichten.

Kurzes Anwendungsbeispiel:


import { eventChannel, END } from 'redux-saga'
import { take, put, call } from 'redux-saga/effects'
function initSocketChannel(query) {
  return eventChannel(emitter => {
      // эмулируем получение данных через web socket
      const handshakeTimeoutId = setTimeout(() => {
          emitter('handshake - ok');
      }, 100);
      const messageTimeoutId = setTimeout(() => {
          emitter('message');
      }, 500);
      const endTimeoutId = setTimeout(() => {
          emitter(END);
      }, 1000);
      // функция отписки от канала
      return () => {
        clearTimeout(handshakeTimeoutId);
        clearTimeout(messageTimeoutId);
        clearTimeout(endTimeoutId);
      }
    }
  )
}
export function* saga() {
  const chan = yield call(initSocketChannel, query)
  try {    
    while (true) {
      const message = yield take(chan);
      // при возвращении каналом END сработает обычный brake
      console.log(`socket : ${message}`)
    }
  } finally {
    console.log('socket terminated')
  }
}

Sicher haben Sie das Vorhandensein einer Konstante bemerkt END- dies ist eine Aktion, die das Ende der Kommunikation mit dem Kanal bedeutet.


Im Quelltext wird Redux-Saga wie folgt dargestellt:


var CHANNEL_END_TYPE = '@@redux-saga/CHANNEL_END';
var END = { type: CHANNEL_END_TYPE };
var isEnd = function isEnd(a) {
  return a && a.type === CHANNEL_END_TYPE;
};

und im Quellcode sehen eventChannelwir das folgende Skript


function eventChannel(subscribe) {
    …
    if (isEnd(input)) {
        close();
        return;
    }
    ...
}

Was ist ein Puffer?


Es ist erwähnenswert, da jeder Kanal einen Basispuffer besitzt, mit dessen Hilfe eine Warteschlange zur Verarbeitung gebildet wird.


Beispiel für die Puffererstellung:


import { buffers } from 'redux-saga'
const buffer = buffers.sliding(5);

buffers - Dies ist die Instanz der Fabrik, um Puffer mit unterschiedlichen Strategien zu erstellen.


Nur 5 Strategien entsprechen den Methoden:


  • buffers.none() - keine Pufferung, neue Nachrichten gehen verloren, wenn keine ausstehenden Teilnehmer vorhanden sind;
  • buffers.fixed(limit)- Neue Nachrichten werden bis zum Limit gepuffert. Ein Überlauffehler führt zu einem Fehler (Ausnahme). Das Standardlimit ist 10;
  • buffers.expanding(initialSize) - Wie fest, aber ein Überlauf bewirkt, dass der Puffer dynamisch erweitert wird.
  • buffers.dropping(limit) - das gleiche wie behoben, aber der Überlauf verwirft die Nachrichten unbemerkt.
  • buffers.sliding(limit) Ist dasselbe wie festgelegt, aber beim Überlauf wird eine neue Nachricht am Ende hinzugefügt und die älteste Nachricht im Puffer gelöscht.

Anstelle von


Wir haben also auseinander genommen, warum der Mechanismus der Kanäle erfunden wurde und welche praktischen Aufgaben verwendet werden.


Hoffentlich entsteht nach dem Lesen eine allgemeine Idee und die Welt ist ein bisschen einfacher geworden.


Jetzt auch beliebt: