Giter Site home page Giter Site logo

Decouple from Redux about redux-saga HOT 23 CLOSED

redux-saga avatar redux-saga commented on May 3, 2024
Decouple from Redux

from redux-saga.

Comments (23)

youknowriad avatar youknowriad commented on May 3, 2024 1

Actually when writing my post, I really thought we should extract the saga runtime from redux-saga, and make something like co, but more generic. I mean a generator runtime in which we can configure how to deal with each type of yielded value :

const myGenerator = function*() {};
const config = function(next, yieldedValue) {
  if  (yieldedValue && typeof yieldedValue.then === 'function') {
    return yieldedValue.then(next);
  }
};
const myRuntime = createRuntime(config);
myRuntime(myGenerator);

I would love to have your opinion about this, and may be a less ambitious option (more pragmatic), is to extract the redux-saga runtime as is in a saga-core lib ?

from redux-saga.

yelouafi avatar yelouafi commented on May 3, 2024

Yes the proc method (the generator driver) is actually decoupled from the middleware itself.

The actual signature is

function proc(iterator, subscribe=()=>()=>{}, dispatch=()=>{}) 

You provide it with an iterator (so you're not only limited to generators). The subscribe is the function used to register a listener for the input events (in redux-saga they are the dispatched actions). Thus you can hookup to different sources (observables, event emitters ...). This is how take(action) is resolved.

The second parameter, dispatch is used to dispatch put(action) effects.

So the function can be used as it is independently. If you are interested I can create a repo with only the proc as main module. And as you mentioned there can be interesting developments for the concept

But for redux-saga I prefer to keep it inside right now. As we're going to experiment with effect monitoring, further development could potentially lead to some coupling with Redux or devtools.

from redux-saga.

slorber avatar slorber commented on May 3, 2024

yes for now it's probably preferable to avoid splitting it into many projects.

I guess for put/take you will follow FSA conventions so if one follow these conventions he would still be able to plug the proc to its own system.

from redux-saga.

yelouafi avatar yelouafi commented on May 3, 2024

I guess for put/take you will follow FSA conventions so if one follow these conventions he would still be able to plug the proc to its own system.

Could you elaborate on this ? I saw the FSA repo as well as how it's used by redux-promise/redux-rx middlewares. But I m not sure if I understood correctly how this can be exploited in redux-saga.

from redux-saga.

slorber avatar slorber commented on May 3, 2024

@yelouafi sorry forget to answer. I just meant that your take effect's matcher assumes there's a "type" attribute on actions which is the attribute name choosen by FSA. (in AtomReact the action has a "name" attribute so I added a "type" to conform to FSA)

from redux-saga.

slorber avatar slorber commented on May 3, 2024

@yelouafi I wonder if it's not worth reopening this issue actually.

As @youknowriad said in his recent medium post, redux-saga is a bit like co but it actually does not execute the effects right now.
Co is a generic library that can be useful on backend and frontend.
https://github.com/tj/co

I see redux-saga being close but more powerful than co, but with too much coupling with Redux. If co is useful to anyone, why Redux-saga could not be?

It really looks like free monad conceptually
http://underscore.io/blog/posts/2015/04/14/free-monads-are-simple.html
I'm not an expert in free monads, but basically it seems to me you have found an elegant way to declare free monad scripts using generators, and provide a default interpreter that solves most usecases.

from redux-saga.

yelouafi avatar yelouafi commented on May 3, 2024

@slorber What you said makes sens. It's just that being a Redux middleware actually. The project has more visibility (I'm really thankfull to @gaearon for all the RTs and mentioning the project in various place). This means it has more chance to be used in real world scenarios. And also benefits from more contributions.

from redux-saga.

yelouafi avatar yelouafi commented on May 3, 2024

@youknowriad a project a with similar idea https://github.com/weo-edu/yio

from redux-saga.

slorber avatar slorber commented on May 3, 2024

cc @joshrtay :)

I like the idea of a generic runtime for IO/async flows. It should provide a nice useful default and be customizable. One should be able to integrate it with redux, or other similar frameworks, and also be able to plug custom logic like to yield observables or whatever maybe...

Also in a free-monad env we may try to make the interpreter optimize IOs (see also dataloader idea to do so). If the runtime user has the ability to plug in its own AST he then can declare an effect like "fetchUser(userId)" instead of the generic effect "fetch("xyz.com/users/$id"), and the interpreter could then optimize / batch userfetching according to the user's needs.

from redux-saga.

joshrtay avatar joshrtay commented on May 3, 2024

Another thing I have been messing around with, which is pretty relevant to this discussion is https://github.com/redux-effects/redux-flo. The idea is one generic control flow middleware that can be leveraged by a whole suite of action creators, other middleware and reducers.

That particular implementation is tied to redux, but the basic idea is that actions are processed in two phases: a map and a toPromise.

toPromise(map(io, control)).then(successHandler, errorHandler)

The map function, maps values over the io function, and handles controls that are generators, arrays, and objects by default. Any control can define it's own behavior by implementing it's own map method. Here's the implementation of map https://github.com/micro-js/map/blob/master/lib/index.js.

The toPromise is basically plucked from co. It converts arrays, objects, generators, and thunks to promises. The only difference between my toPromise and the one in co is that it's not recursive (maybe it should be). Here's the implementation of toPromise https://github.com/micro-js/to-promise.

Love to hear what you guys think.

Edit:

The motivation behind the toPromise(map(io, control)) paradigm is to try and completely generalize IO/async flows. An indication of how general this paradigm is that both yio and co can be roughly implemented with it in one line:

yio is toPromise(map(io, flatten(control)))
co is toPromise(map(toPromise, flatten(control)))

from redux-saga.

joshrtay avatar joshrtay commented on May 3, 2024

On a side note, I think that redux middleware is a really powerful way of creating interpreters. I don't know that it's as important to decouple the middleware api from saga as it is to be able to leverage redux-saga in non-redux contexts. I've created a little package that let's you do just that. It's called bind-middleware and it basically composes redux middleware and binds them to a ctx. Here's an example:

var bindMiddleware = require('@f/bind-middleware')

var logger = require('redux-logger')
var thunk = require('redux-thunk')

var dispatch = bindMiddleware([
  thunk,
  logger()
])

dispatch(dispatch => {
  setTimeout(() => {
    dispatch({type: 'INCREMENT'})
  })
})

// log => {type: `INCREMENT`}

I've started toying around with using bind-middleware with redux-flo for IO on the server. Makes for very testable code.

from redux-saga.

yelouafi avatar yelouafi commented on May 3, 2024

@joshrtay looked rapidely at this and I m really impressed! The concept of mapping/reducing a generator is both simple and powerful. And I like how you leverage composition in your code.

from redux-saga.

yelouafi avatar yelouafi commented on May 3, 2024

One difference though is that you still fire the flow on each action. While Redux saga treats incoming actions themselves as part of the flow.

from redux-saga.

joshrtay avatar joshrtay commented on May 3, 2024

@yelouafi Let me think about it a little bit... I think however, that redux-saga with support for take, put, and delay can be implemented in just a few lines with the help of redux-flo.

from redux-saga.

youknowriad avatar youknowriad commented on May 3, 2024

Using promises in the runtime core can lead to some weird bugs like this one #50

I did a small experiment of I can imagine being a generic generator runtime without using promises to avoid synchronisation problems, you can check here.

https://github.com/youknowriad/generator-runtime-experiment

What do you think ?

from redux-saga.

joshrtay avatar joshrtay commented on May 3, 2024

K. This is definitely going to be a bit more than a few lines :)

But here's a really modular way of implementing sagas with on support. Take-put and delay can similarly be implemented as their own middleware. And then saga can package it all together with something like @f/compose-middleware.

sagas

import {flo} from 'redux-flo'

function middleware (...sagas) {
  return ({dispatch}) => {
    dispatch(flo(sagas.map(flo))).then(res => console.log('all sagas exited'))
    return next => action => next(action)
  }   
}

export default middleware

on

import {flo} from 'redux-flo'
import compose from '@f/compose'
import PromiseEmitter from 'promise-emitter' // this doesn't exist 

const ON = 'ON'

function middleware () {
  let pemitter = new PromiseEmitter()
  return ({dispatch}) => next => action =>
    action && action.type === ON
      ? pemitter.on(action.payload.event, compose(flo, action.payload.listener))
      : (pemitter.emit(action.type, action.payload) || next(action))
}

function on (event, listener) {
  return {type: ON, payload: {event, listener}}
}

export default middleware
export {
  on
}

saga

import compose from '@f/compose-middleware'
import flow from 'redux-flo'
import sagasw from 'sagas'
import onw from 'on'

function middleware (...sagas) {
  return compose([flow(), sagasw(...sagas), onw()])
}

export default middleware

app

import sagas from 'sagas'
import bind from '@f/bind-middleware'

let dispatch = bind([sagas(rootSaga)])

dispatch({type: 'SOME_ACTION'})

function * rootSaga () {
   yield on('SOME_ACTION', handlerSaga)
}

This is all very rough and I'm not very familiar with saga, but treating incoming actions as part of the flow should not be a problem. Let me know if I missed something.

from redux-saga.

yelouafi avatar yelouafi commented on May 3, 2024

@youknowriad seems interesting. Will take a deeper look later. Is the match func necesary ? You can return the boolean directly from resolve to indicate a successful match

from redux-saga.

yelouafi avatar yelouafi commented on May 3, 2024

@joshrtay not sure to undrestand what the code do. But redux-saga has to take care of some extra things like cancellation propagation, which is a little bif too awkward to express by the map/reduce pattern (or at least i have to think about it).

Also I like the idea of control flow units expressed as middlewares

from redux-saga.

joshrtay avatar joshrtay commented on May 3, 2024

@yelouafi The code is just a basic implementation of saga with only on support. The sagas module is middleware that simply runs the saga daemons. The on module attempts to simply treat the control flow units of saga as redux-effects (middleware that returns promises). The saga module composes flow, saga, and on into a single middleware and would ultimately add take, put... in a fully fleshed out version. app is just a stupid example of an app using sagas nothing special and probably not even relevant.

I don't think canceling propagation would have to do anything that special, but I too would have to think about it.

The main idea here is come up with a powerful model for control flow (whether it be redux-flo or something else), stick it at the top of the middleware stack, create middleware for starting your sagas, then everything else is redux-effects style middleware and actions.

from redux-saga.

youknowriad avatar youknowriad commented on May 3, 2024

@yelouafi you're absolutely right, just made the replacement. I don't handle all the uses cases (race, cancel) etc... and errors are not yet handled correctly but It seems very doable to me, I'll try to add this stuff later.

from redux-saga.

yelouafi avatar yelouafi commented on May 3, 2024

@youknowriad FYI rewrote the redux-saga core without promises. It was necessary to get rid of all mentioned issues and have a predictable order of things, the code is the no-promise branch. Trickiest part was to propagate cancellations (sort of control flow inversion), but finally works, and all tests cases pass.

from redux-saga.

youknowriad avatar youknowriad commented on May 3, 2024

@yelouafi great, good news, even if the promise code looks nicer, I think this is the right choice to avoid any unwanted side effect :)

from redux-saga.

yelouafi avatar yelouafi commented on May 3, 2024

Not sut but maybe I'll make a separate repo to expriment with other things (csp channels, maybe actors. if i've got the time of course).

from redux-saga.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.